Файл remove-files.js:
var fs = require('fs');
var doNotRemoveFiles = [
'remove-files.js',
'node_modules',
'@types',
'dist',
'docs',
'src',
'tests',
'.gitignore',
'.npmignore',
'.npmrc',
'.stylelintrc',
'karma.conf.js',
'package.json',
'postcss.config.js',
'readme.md',
'tsconfig.json',
'tslint.json',
'webpack.config.js',
];
fs.readdir('./', function (error, fileNames) {
if (error) {throw error;}
var fileNamesCounter = fileNames.length;
fileNames.forEach(function (fileName) {
var len = doNotRemoveFiles.length;
while (len--) {
if (doNotRemoveFiles[len] === fileName) {return;}
}
fs.stat('./' + fileName, function (error, stats) {
if (stats.isFile()) {
fs.unlink('./' + fileName, function (error) {
if (error) {throw error;}
fileNamesCounter--;
if (fileNamesCounter === 0) {
console.log('DONE');
}
});
} else if (stats.isDirectory()) {
fs.rmdir('./' + fileName, function (error) { // Remove empty "etc" directory.
if (error) {throw error;}
fileNamesCounter--;
if (fileNamesCounter === 0) {
console.log('DONE');
}
});
}
});
});
});
четверг, 8 августа 2019 г.
Node.js UUID
Файл uuid.js:
var crypto = require('crypto');
function rng () {
return crypto.randomBytes(16);
}
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1);
}
function bytesToUuid (buf, offset) {
var i = offset || 0;
var bth = byteToHex;
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
return [
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]]
].join('');
}
function uuid (options, buf, offset) {
var i = buf && offset || 0;
if (typeof options === 'string') {
buf = options === 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
// Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
return buf || bytesToUuid(rnds);
}
Файл index.js:
var uuid = require('./uuid');
console.log(uuid());
module.exports = uuid;
var crypto = require('crypto');
function rng () {
return crypto.randomBytes(16);
}
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1);
}
function bytesToUuid (buf, offset) {
var i = offset || 0;
var bth = byteToHex;
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
return [
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]]
].join('');
}
function uuid (options, buf, offset) {
var i = buf && offset || 0;
if (typeof options === 'string') {
buf = options === 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
// Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
return buf || bytesToUuid(rnds);
}
Файл index.js:
var uuid = require('./uuid');
console.log(uuid());
module.exports = uuid;
Node.js CLI Spinner
Файл spinner.js
function spinner () {
var sequence = ['|', '/', '-', '\\'];
var index = 0;
var intervalId;
return {
start: function (doNotBlockNodeJsProcess) {
index = 0;
process.stdout.write(sequence[index]);
intervalId = setInterval(function () {
process.stdout.write(sequence[index].replace(/./g, '\b'));
if (index < sequence.length - 1) {
index = index + 1;
} else {
index = 0;
}
process.stdout.write(sequence[index]);
}, 100);
if (doNotBlockNodeJsProcess) {
intervalId.unref();
}
},
stop: function () {
clearInterval(intervalId);
process.stdout.write(sequence[index].replace(/./g, '\b'));
}
};
}
module.exports = spinner;
Файл index.js
var spinner = require('spinner')();
spinner.start(true);
setTimeout(function () {
spinner.stop();
}, 5000);
function spinner () {
var sequence = ['|', '/', '-', '\\'];
var index = 0;
var intervalId;
return {
start: function (doNotBlockNodeJsProcess) {
index = 0;
process.stdout.write(sequence[index]);
intervalId = setInterval(function () {
process.stdout.write(sequence[index].replace(/./g, '\b'));
if (index < sequence.length - 1) {
index = index + 1;
} else {
index = 0;
}
process.stdout.write(sequence[index]);
}, 100);
if (doNotBlockNodeJsProcess) {
intervalId.unref();
}
},
stop: function () {
clearInterval(intervalId);
process.stdout.write(sequence[index].replace(/./g, '\b'));
}
};
}
module.exports = spinner;
Файл index.js
var spinner = require('spinner')();
spinner.start(true);
setTimeout(function () {
spinner.stop();
}, 5000);
XHR Test request in browser console
var data = {a: 1, b: 2};
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://site.com/send/data', false);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
if (xhr.status !== 200) {
console.log('ERROR');
} else {
console.log(xhr.responseText);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://site.com/send/data', false);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
if (xhr.status !== 200) {
console.log('ERROR');
} else {
console.log(xhr.responseText);
}
понедельник, 15 июля 2019 г.
Создание Node.js HTTPS, TLS/SSL сертификатов.
Для работы с HTTPS, TLS/SSL необходимо произвести следующие действия:
1) Создать "центр сертификации" (CA - Certificate Authority), который позволит создавать и проверять "самоподписанные сертфикаты" (Self-Signed SSL Certificate).
2) Создать "приватный ключ" (Private key) для подписи сертификатов.
3) С помощью "приватного ключа" (Private key) создать "запрос на подпись сертификата" (CSR - Certificate signing request).
4) С помощью "приватного ключа" (Private key) и "запроса на подпись сертификата" (CSR - Certificate signing request) создать сам "сертификат" (Cert - Certificate).
Создание "центра сертификации" (Certificate Authority).
Для упрощения процесса скопируйте эти настройки в файл "ca.cnf" и пропишите в них ваши значения.
[ ca ]
default_ca = CA_default
[ CA_default ]
serial = ca-serial
crl = ca-crl.pem
database = ca-database.txt
name_opt = CA_default
cert_opt = CA_default
default_crl_days = 9999
default_md = md5
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
output_password = password
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = ca
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = test
Далее в командной строке вашего терминала выполните такую команду:
openssl req -new -x509 -days 9999 -config ca.cnf -keyout ca-key.pem -out ca-crt.pem
В результате вы создадите файлы "центра сертификации": ca-key.pem и ca-crt.pem.
Теперь создадим "приватный ключ" (Private key) для сервера Node.js с помощью следующей команды:
openssl genrsa -out server-key.pem 4096
В результате вы создадите файл server-key.pem.
Теперь создадим "запрос на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "server.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = localhost
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
После этого выполните команду:
openssl req -new -config server.cnf -key server-key.pem -out server-csr.pem
В результате вы создадите файл server-csr.pem.
Далее подпишем "запрос на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итогового "сертификата" (Cert - Certificate) нашего Node.js HTTPS, TLS/SSL сервера, выполнив команду:
openssl x509 -req -extfile server.cnf -days 999 -passin "pass:password" -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem
Обратите внимание на заданный в команде пароль: "pass:password".
В результате вы создадите файл server-crt.pem.
Теперь созданный сертификат готов для использования не сервере.
Создадим файл "server.js" и пропишем в нем данный код для создания HTTPS сервера:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.method + ' '+ req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443, '127.0.0.1');
Проверим, как работает наш сервер при получения запроса от браузера, перейдя в браузере по адресу "127.0.0.1:443".
Не забудьте добавить в вашу операционную систему созданный нами "центр сертификации" (CA - Certificate Authority) путем установки файла "ca-crt.pem" и пометки его, как доверенного.
После проверки работы сервера через браузер создадим HTTPS-клиент для Node.js, который мог бы соединяться с нашим HTTPS-сервером c проверкой сертификата сервера через созданный нами "центр сертификации" (CA - Certificate Authority).
Создадим файл "client.js" и пропишем в нем данный код для создания HTTPS клиента:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function (data) {
process.stdout.write(data);
});
});
req.end();
Обратите внимание, что в options мы добавили свойство "ca", в котором прописан наш файл "центра сертификации" (CA - Certificate Authority) для того, чтобы наш клиент мог доверять "публичному ключу" (Public key), который он получит от сервера при подключении к нему.
Теперь выполните код подключения клиента к серверу и в консоли командной строки вы увидите надпись: "Hello world!".
Далее создадим свой сертификат для клиента, чтобы он тоже мог с помощью него представляться серверу.
Мы сделаем два сертификата для того, чтобы иметь возможность делать запросы к серверу из двух разных клиентов.
Создадим каждому клиенту свой "приватный ключ" (Private key) командами:
openssl genrsa -out client1-key.pem 4096
openssl genrsa -out client2-key.pem 4096
В результате вы создадите файл client1-key.pem и файл client2-key.pem.
Далее создадим два "запроса на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "client1.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client1
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
А настройки ниже скопируйте в файл "client2.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client2
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
"Запросы на подпись сертификата" (CSR - Certificate signing request) создадим с помощью пары команд:
openssl req -new -config client1.cnf -key client1-key.pem -out client1-csr.pem
openssl req -new -config client2.cnf -key client2-key.pem -out client2-csr.pem
В результате вы создадите файл client1-csr.pem и файл client2-csr.pem.
Теперь подпишем "запросы на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итоговых "сертификатов" (Cert - Certificate) наших Node.js HTTPS, TLS/SSL клиентов, выполнив команду:
openssl x509 -req -extfile client1.cnf -days 999 -passin "pass:password" -in client1-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client1-crt.pem
openssl x509 -req -extfile client2.cnf -days 999 -passin "pass:password" -in client2-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client2-crt.pem
В результате вы создадите файл client1-crt.pem и файл client2-crt.pem.
Для того, чтобы убедиться, что мы все сделали верно проверим наши сертификаты, выполнив команды:
openssl verify -CAfile ca-crt.pem client1-crt.pem
openssl verify -CAfile ca-crt.pem client2-crt.pem
Такую же проверку вы можете сделать и для сертификата, который вы создали для сервера, выполнив команду:
openssl verify -CAfile ca-crt.pem server-crt.pem
Если вы все сделали верно, то вы получите "OK" в результате проверки.
Теперь напишем код сервера и клиента, использующего каждый свой сертификат.
Добавим в код сервера свойства requestCert и rejectUnauthorized, требующие, чтобы клиент всегда предоставлял серверу свой сертификат, изменив код в файле "server.js" так:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.socket.getPeerCertificate().subject.CN + ' ' + req.method + ' ' + req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443);
Теперь, если вы попробуйте подключиться к серверу из клиента, то подключение клиента будет отвергнуто сервером, поскольку в данный момент клиент еще не умеет передавать серверу свой сертификат.
В итоге Node.js выдаст сообщение об ошибке "socket hangup error" из-за того, что TLS не будет соблюдено.
Поэтому, чтобы все заработало мы добавим в код клиента в файле "client.js" опции key и cert так:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
key: fs.readFileSync('client1-key.pem'),
cert: fs.readFileSync('client1-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function(data) {
process.stdout.write(data);
});
});
req.on('error', function(e) {
console.error(e);
});
req.end();
1) Создать "центр сертификации" (CA - Certificate Authority), который позволит создавать и проверять "самоподписанные сертфикаты" (Self-Signed SSL Certificate).
2) Создать "приватный ключ" (Private key) для подписи сертификатов.
3) С помощью "приватного ключа" (Private key) создать "запрос на подпись сертификата" (CSR - Certificate signing request).
4) С помощью "приватного ключа" (Private key) и "запроса на подпись сертификата" (CSR - Certificate signing request) создать сам "сертификат" (Cert - Certificate).
Создание "центра сертификации" (Certificate Authority).
Для упрощения процесса скопируйте эти настройки в файл "ca.cnf" и пропишите в них ваши значения.
[ ca ]
default_ca = CA_default
[ CA_default ]
serial = ca-serial
crl = ca-crl.pem
database = ca-database.txt
name_opt = CA_default
cert_opt = CA_default
default_crl_days = 9999
default_md = md5
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
output_password = password
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = ca
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = test
Далее в командной строке вашего терминала выполните такую команду:
openssl req -new -x509 -days 9999 -config ca.cnf -keyout ca-key.pem -out ca-crt.pem
В результате вы создадите файлы "центра сертификации": ca-key.pem и ca-crt.pem.
Теперь создадим "приватный ключ" (Private key) для сервера Node.js с помощью следующей команды:
openssl genrsa -out server-key.pem 4096
В результате вы создадите файл server-key.pem.
Теперь создадим "запрос на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "server.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = localhost
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
После этого выполните команду:
openssl req -new -config server.cnf -key server-key.pem -out server-csr.pem
В результате вы создадите файл server-csr.pem.
Далее подпишем "запрос на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итогового "сертификата" (Cert - Certificate) нашего Node.js HTTPS, TLS/SSL сервера, выполнив команду:
openssl x509 -req -extfile server.cnf -days 999 -passin "pass:password" -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem
Обратите внимание на заданный в команде пароль: "pass:password".
В результате вы создадите файл server-crt.pem.
Теперь созданный сертификат готов для использования не сервере.
Создадим файл "server.js" и пропишем в нем данный код для создания HTTPS сервера:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.method + ' '+ req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443, '127.0.0.1');
Проверим, как работает наш сервер при получения запроса от браузера, перейдя в браузере по адресу "127.0.0.1:443".
Не забудьте добавить в вашу операционную систему созданный нами "центр сертификации" (CA - Certificate Authority) путем установки файла "ca-crt.pem" и пометки его, как доверенного.
После проверки работы сервера через браузер создадим HTTPS-клиент для Node.js, который мог бы соединяться с нашим HTTPS-сервером c проверкой сертификата сервера через созданный нами "центр сертификации" (CA - Certificate Authority).
Создадим файл "client.js" и пропишем в нем данный код для создания HTTPS клиента:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function (data) {
process.stdout.write(data);
});
});
req.end();
Обратите внимание, что в options мы добавили свойство "ca", в котором прописан наш файл "центра сертификации" (CA - Certificate Authority) для того, чтобы наш клиент мог доверять "публичному ключу" (Public key), который он получит от сервера при подключении к нему.
Теперь выполните код подключения клиента к серверу и в консоли командной строки вы увидите надпись: "Hello world!".
Далее создадим свой сертификат для клиента, чтобы он тоже мог с помощью него представляться серверу.
Мы сделаем два сертификата для того, чтобы иметь возможность делать запросы к серверу из двух разных клиентов.
Создадим каждому клиенту свой "приватный ключ" (Private key) командами:
openssl genrsa -out client1-key.pem 4096
openssl genrsa -out client2-key.pem 4096
В результате вы создадите файл client1-key.pem и файл client2-key.pem.
Далее создадим два "запроса на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "client1.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client1
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
А настройки ниже скопируйте в файл "client2.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client2
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
"Запросы на подпись сертификата" (CSR - Certificate signing request) создадим с помощью пары команд:
openssl req -new -config client1.cnf -key client1-key.pem -out client1-csr.pem
openssl req -new -config client2.cnf -key client2-key.pem -out client2-csr.pem
В результате вы создадите файл client1-csr.pem и файл client2-csr.pem.
Теперь подпишем "запросы на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итоговых "сертификатов" (Cert - Certificate) наших Node.js HTTPS, TLS/SSL клиентов, выполнив команду:
openssl x509 -req -extfile client1.cnf -days 999 -passin "pass:password" -in client1-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client1-crt.pem
openssl x509 -req -extfile client2.cnf -days 999 -passin "pass:password" -in client2-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client2-crt.pem
В результате вы создадите файл client1-crt.pem и файл client2-crt.pem.
Для того, чтобы убедиться, что мы все сделали верно проверим наши сертификаты, выполнив команды:
openssl verify -CAfile ca-crt.pem client1-crt.pem
openssl verify -CAfile ca-crt.pem client2-crt.pem
Такую же проверку вы можете сделать и для сертификата, который вы создали для сервера, выполнив команду:
openssl verify -CAfile ca-crt.pem server-crt.pem
Если вы все сделали верно, то вы получите "OK" в результате проверки.
Теперь напишем код сервера и клиента, использующего каждый свой сертификат.
Добавим в код сервера свойства requestCert и rejectUnauthorized, требующие, чтобы клиент всегда предоставлял серверу свой сертификат, изменив код в файле "server.js" так:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.socket.getPeerCertificate().subject.CN + ' ' + req.method + ' ' + req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443);
Теперь, если вы попробуйте подключиться к серверу из клиента, то подключение клиента будет отвергнуто сервером, поскольку в данный момент клиент еще не умеет передавать серверу свой сертификат.
В итоге Node.js выдаст сообщение об ошибке "socket hangup error" из-за того, что TLS не будет соблюдено.
Поэтому, чтобы все заработало мы добавим в код клиента в файле "client.js" опции key и cert так:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
key: fs.readFileSync('client1-key.pem'),
cert: fs.readFileSync('client1-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function(data) {
process.stdout.write(data);
});
});
req.on('error', function(e) {
console.error(e);
});
req.end();
После этого выполните подключение клиента к серверу и сервер в ответ вернет "Hello world!".
В консоли сервера в этом случае вы увидите информации о подключении клиента и его "Common Name" из переданного серверу сертификата.
Поменяйте в коде клиента сертификат client1 на сертификат client2 и вы увидите, что клиент со вторым сертификатом также подключается к серверу.
Теперь проведем "отзыв сертификата" (CR - Certificate Revocation).
Для отзыва второго сертификата клиента мы создадим "список отозванных сертификатов" (CRL - Certificate Revocation List).
Чтобы это сделать создайте пустой текстовый файл "ca-database.txt", символизирующий базу данных.
Далее выполните команду для отзыва второго сертификата клиента client2-crt.pem:
openssl ca -revoke client2-crt.pem -keyfile ca-key.pem -config ca.cnf -cert ca-crt.pem -passin "pass:password"
Обратите внимание на заданный в команде пароль: "pass:password".
Обратите внимание на заданный в команде пароль: "pass:password".
После этого выполните команду для обновления "списка отозванных сертификатов" (CRL - Certificate Revocation List):
openssl ca -keyfile ca-key.pem -cert ca-crt.pem -config ca.cnf -gencrl -out ca-crl.pem -passin "pass:password"
Наконец добавьте в опции сервера свойство crl для получения доступа к "списку отозванных сертификатов" (CRL - Certificate Revocation List), изменив код так:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
crl: fs.readFileSync('ca-crl.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, function (req, res) {
console.log(new Date()+ ' ' + req.connection.remoteAddress + ' ' + req.socket.getPeerCertificate().subject.CN + ' ' + req.method + ' ' + req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443);
После этого сервер будет учитывать "список отозванных сертификатов" (CRL - Certificate Revocation List) и принимать запрос от клиента с сертификатом client1-crt.pem и отвергать запросы клиента с сертификатом client2-crt.pem.
Таким образом, мы научились создавать самоподписанные сертификаты для сервера и проверять сертификаты, приходящие от клиентов. Дополнительно к этому мы научились отзывать сертификаты.
Поскольку в сертификатах содержится имя клиента Common Name, то мы также можем использовать их для идентификации каждого клиента на нашем сервере.
вторник, 9 июля 2019 г.
Node.js - Simple Stream and Generator Demos
Simple Stream.
const {Readable, Writable} = require('stream');
// Reader
const createCounterReader = () => {
let count = 0;
return new Readable({
objectMode: true,
read () {
count += 1;
console.log('reading:', count);
this.push(count);
}
});
};
const counterReader = createCounterReader();
// Writer
const logWriter = new Writable({
objectMode: true,
write: (chunk, _, done) => {
setTimeout(() => {
console.log('writing:', chunk);
done();
}, 100);
}
});
// Pipe reader to Writer
counterReader.pipe(logWriter);
Stream for async counter.
Stream for async counter.
const {Readable, Writable} = require('stream');
const createCounterReader = (delay) => {
let counter = 0;
const reader = new Readable({
objectMode: true,
read () {}
});
setInterval(() => {
counter += 1;
console.log('reading:', counter);
reader.push(counter);
}, delay);
return reader;
};
const counterReader = createCounterReader(1000);
const logWriter = new Writable({
objectMode: true,
write: (chunk, _, done) => {
console.log('writing:', chunk);
done();
}
});
counterReader.pipe(logWriter);
Stream for async counter writer.
const {Readable, Writable} = require('stream');
const createCounterReader = (delay) => {
let counter = 0;
const reader = new Readable({
objectMode: true,
read () {}
});
setInterval(() => {
counter += 1;
console.log('reading:', counter);
reader.push(counter);
}, delay);
return reader;
};
const counterReader = createCounterReader(1000);
const logWriter = new Writable({
objectMode: true,
write: (chunk, _, done) => {
setTimeout(() => {
console.log('writing:', chunk);
done();
}, 5 * 1000);
}
});
counterReader.pipe(logWriter);
Simple Generator Sync.
function* counterGenerator() {
let count = 0;
while (true) {
count += 1;
console.log('reading:', count);
yield count;
}
}
const counterIterator = counterGenerator();
const logIterator = (iterator) => {
for (const item of iterator) {
console.log('writing:', item);
}
};
logIterator(counterIterator);
Simple Generator Async.
function* counterGenerator() {
let count = 0;
while (true) {
count += 1;
console.log('reading:', count);
yield count;
}
}
const counterIterator = counterGenerator();
const logIterator = async (iterator) => {
for (const item of iterator) {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 100);
});
console.log('writing:', item);
}
};
logIterator(counterIterator);
Async Generator Counter.
Async Generator Counter.
async function* counterGenerator(delay) {
let counter = 0;
while (true) {
await new Promise(r => setTimeout(r, delay));
counter += 1;
console.log('reading:', counter);
yield counter;
}
}
const counterIterator = counterGenerator(1000);
const logIterator = async (iterator) => {
for await (const item of iterator) {
console.log('writing:', item);
}
};
logIterator(counterIterator);
Async Generator Counter for Async Interator.
async function* counterGenerator(delay) {
let counter = 0;
while (true) {
await new Promise(r => setTimeout(r, delay));
counter += 1;
console.log('reading:', counter);
yield counter;
}
}
const counterIterator = counterGenerator(1000);
const logIterator = async (iterator) => {
for await (const item of iterator) {
console.log('writing:', item);
await new Promise(r => setTimeout(r, 5 * 1000));
}
};
logIterator(counterIterator);
четверг, 27 июня 2019 г.
Node.js - Https server and client
https-server.js
var https = require('https');
var url = require('url');
/////////////////////////////////////////////////////////////////////
var forge = require('node-forge'); // npm install node-forge
var keyPair = forge.pki.rsa.generateKeyPair(2048);
var cert = forge.pki.createCertificate();
var now = new Date();
var oneYear = new Date(new Date(now).setFullYear(now.getFullYear() + 1));
Object.assign(cert, {
publicKey: keyPair.publicKey,
serialNumber: '01',
validity: {
notBefore: now,
notAfter: oneYear
}
});
cert.sign(keyPair.privateKey, forge.md.sha256.create()); // self signed
var serverPrivateKeyPem = forge.pki.privateKeyToPem(keyPair.privateKey);
var serverPublicKeyPem = forge.pki.publicKeyToPem(keyPair.publicKey);
var serverCertificatePem = forge.pki.certificateToPem(cert);
var options = {
key: serverPrivateKeyPem
cert: serverCertificatePem
};
/////////////////////////////////////////////////////////////////////
var server = https.createServer(options);
server.on('connection', function (socket) {
console.log('Клиент установил socket-соединение с сервером.');
});
server.on('request', function (request, response) {
console.log(request.httpVersion);
console.log(request.method);
console.log(url.parse(request.url));
console.log(request.rawHeaders);
console.log(request.headers);
console.log(request.rawTrailers);
request.on('aborted', function () {
console.log('Клиент прервал свое соединение с сервером.');
if (request.aborted) {console.log('Соединение с сервером прервано клиентом.');}
});
request.on('close', function () {
console.log('Клиент закрыл свое соединение с сервером.');
});
request.on('data', function (data) {});
request.on('end', function (data) {
console.log(request.trailers);
if (request.complete) {console.log('Клиент передал серверу все данные.');}
response.on('finish', function () {
console.log('Сервер завершил отправку данных клиенту.');
});
response.on('close', function () {
console.log('Сервер закрыл соединение с клиентом.');
});
console.log('Ваш IP-адрес ' + response.socket.remoteAddress + ' и порт ' + response.socket.remotePort);
response.statusCode = 404;
response.statusMessage = 'Not found';
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
var headerNames = response.getHeaderNames();
var headers = response.getHeaders();
var setCookie = response.getHeader('set-cookie');
if (response.hasHeader('set-cookie')) {console.log('Заголовок Set-Cookie установлен.');}
response.removeHeader('Set-Cookie');
response.sendDate = false; // Не посылать заголовок с датой клиенту.
response.writeHead(200, { 'Content-Type': 'text/plain', 'Trailer': 'Content-MD5' });
if (response.headersSent) {console.log('Заголовки были отправлены клиенту.');}
response.write('<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>Главная страница</title></head><body><h1>Привет!</h1></body></html>');
response.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});
response.end();
if (response.finished) {console.log('Сервер отправил все данные клиенту.');}
});
});
server.on('checkExpectation', function (request, response) {
console.log('От клиента был получен HTTP-заголовок "Expect" со значением не равным 100-continue.');
});
server.on('checkContinue', function (request, response) {
console.log('От клиента был получен HTTP-заголовок "Expect: 100-continue".');
response.writeContinue();
});
server.on('clientError', function (error, socket) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.on('close', function () {
console.log('Сервер закрылся.');
});
server.on('error', function (error) {
console.log('На сервер произошла ошибка.');
server.close();
});
server.listen(443, '127.0.0.1', function () {
console.log('Серер стартовал по адресу 127.0.0.1:443');
});
if (server.listening) {console.log('Сервер прослушивает входящие соединения.');}
https-client.js
var http = require('http');
var https = require('https');
var querystring = require('querystring');
var postData = querystring.stringify({'msg': 'Hello World!'});
var options = {
/////////////////////////////////////////////////////////////////////
rejectUnauthorized: false, // don't check certificate
requestCert: true,
/////////////////////////////////////////////////////////////////////
method: 'POST',
protocol: 'https:',
auth: 'user:password',
hostname: '127.0.0.1',
port: 443,
path: '/page/upload.html',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
var client = http.request(options);
client.on('response', function (response) {
console.log('Код статуса ответа сервера: ' + response.statusCode);
console.log('Статус ответа сервера: ' + http.STATUS_CODES[response.statusCode]);
console.log('Заголовки переданные сервером: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (data) {console.log('Ответ от сервера: ' + data);});
response.on('end', function (data) {console.log('Передача данных от сервера завершена.');});
});
client.setTimeout(1000);
client.on('timeout', function () {
client.abort();
if (client.aborted) {console.log('Клиент отменил соединение с сервером.');}
});
client.on('abort', function () {
console.log('Клиент отменил соединение с сервером.');
});
client.on('error', function (error) {
console.log('Произошла ошибка при выполнении клиентского запроса на сервер.');
throw error;
});
client.on('continue', function () {
console.log('Сервер прислал HTTP-заголовок "100 Continue" в ответ на заголовок "Expect: 100-continue", отправленный клиентом на сервер.');
console.log('Клиенту следует послать на сервер тело запроса.');
});
client.setHeader('Cookie', ['type=ninja', 'language=javascript']);
var cookie = client.getHeader('Cookie');
client.removeHeader('Cookie');
client.write(postData);
client.end();
if (client.finished) {console.log('Отправка данных на сервер завершена.');}
var https = require('https');
var url = require('url');
/////////////////////////////////////////////////////////////////////
var forge = require('node-forge'); // npm install node-forge
var keyPair = forge.pki.rsa.generateKeyPair(2048);
var cert = forge.pki.createCertificate();
var now = new Date();
var oneYear = new Date(new Date(now).setFullYear(now.getFullYear() + 1));
Object.assign(cert, {
publicKey: keyPair.publicKey,
serialNumber: '01',
validity: {
notBefore: now,
notAfter: oneYear
}
});
cert.sign(keyPair.privateKey, forge.md.sha256.create()); // self signed
var serverPrivateKeyPem = forge.pki.privateKeyToPem(keyPair.privateKey);
var serverPublicKeyPem = forge.pki.publicKeyToPem(keyPair.publicKey);
var serverCertificatePem = forge.pki.certificateToPem(cert);
var options = {
key: serverPrivateKeyPem
cert: serverCertificatePem
};
/////////////////////////////////////////////////////////////////////
var server = https.createServer(options);
server.on('connection', function (socket) {
console.log('Клиент установил socket-соединение с сервером.');
});
server.on('request', function (request, response) {
console.log(request.httpVersion);
console.log(request.method);
console.log(url.parse(request.url));
console.log(request.rawHeaders);
console.log(request.headers);
console.log(request.rawTrailers);
request.on('aborted', function () {
console.log('Клиент прервал свое соединение с сервером.');
if (request.aborted) {console.log('Соединение с сервером прервано клиентом.');}
});
request.on('close', function () {
console.log('Клиент закрыл свое соединение с сервером.');
});
request.on('data', function (data) {});
request.on('end', function (data) {
console.log(request.trailers);
if (request.complete) {console.log('Клиент передал серверу все данные.');}
response.on('finish', function () {
console.log('Сервер завершил отправку данных клиенту.');
});
response.on('close', function () {
console.log('Сервер закрыл соединение с клиентом.');
});
console.log('Ваш IP-адрес ' + response.socket.remoteAddress + ' и порт ' + response.socket.remotePort);
response.statusCode = 404;
response.statusMessage = 'Not found';
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
var headerNames = response.getHeaderNames();
var headers = response.getHeaders();
var setCookie = response.getHeader('set-cookie');
if (response.hasHeader('set-cookie')) {console.log('Заголовок Set-Cookie установлен.');}
response.removeHeader('Set-Cookie');
response.sendDate = false; // Не посылать заголовок с датой клиенту.
response.writeHead(200, { 'Content-Type': 'text/plain', 'Trailer': 'Content-MD5' });
if (response.headersSent) {console.log('Заголовки были отправлены клиенту.');}
response.write('<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>Главная страница</title></head><body><h1>Привет!</h1></body></html>');
response.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});
response.end();
if (response.finished) {console.log('Сервер отправил все данные клиенту.');}
});
});
server.on('checkExpectation', function (request, response) {
console.log('От клиента был получен HTTP-заголовок "Expect" со значением не равным 100-continue.');
});
server.on('checkContinue', function (request, response) {
console.log('От клиента был получен HTTP-заголовок "Expect: 100-continue".');
response.writeContinue();
});
server.on('clientError', function (error, socket) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.on('close', function () {
console.log('Сервер закрылся.');
});
server.on('error', function (error) {
console.log('На сервер произошла ошибка.');
server.close();
});
server.listen(443, '127.0.0.1', function () {
console.log('Серер стартовал по адресу 127.0.0.1:443');
});
if (server.listening) {console.log('Сервер прослушивает входящие соединения.');}
https-client.js
var http = require('http');
var https = require('https');
var querystring = require('querystring');
var postData = querystring.stringify({'msg': 'Hello World!'});
var options = {
/////////////////////////////////////////////////////////////////////
rejectUnauthorized: false, // don't check certificate
requestCert: true,
/////////////////////////////////////////////////////////////////////
method: 'POST',
protocol: 'https:',
auth: 'user:password',
hostname: '127.0.0.1',
port: 443,
path: '/page/upload.html',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
var client = http.request(options);
client.on('response', function (response) {
console.log('Код статуса ответа сервера: ' + response.statusCode);
console.log('Статус ответа сервера: ' + http.STATUS_CODES[response.statusCode]);
console.log('Заголовки переданные сервером: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (data) {console.log('Ответ от сервера: ' + data);});
response.on('end', function (data) {console.log('Передача данных от сервера завершена.');});
});
client.setTimeout(1000);
client.on('timeout', function () {
client.abort();
if (client.aborted) {console.log('Клиент отменил соединение с сервером.');}
});
client.on('abort', function () {
console.log('Клиент отменил соединение с сервером.');
});
client.on('error', function (error) {
console.log('Произошла ошибка при выполнении клиентского запроса на сервер.');
throw error;
});
client.on('continue', function () {
console.log('Сервер прислал HTTP-заголовок "100 Continue" в ответ на заголовок "Expect: 100-continue", отправленный клиентом на сервер.');
console.log('Клиенту следует послать на сервер тело запроса.');
});
client.setHeader('Cookie', ['type=ninja', 'language=javascript']);
var cookie = client.getHeader('Cookie');
client.removeHeader('Cookie');
client.write(postData);
client.end();
if (client.finished) {console.log('Отправка данных на сервер завершена.');}
Node.js - Net server and client
net-is-ip.js
var net = require('net');
console.log('Это IP-адрес ? ' + (net.isIP('1234') === 0 ? false : true));
console.log('Это IP-адрес в формате IPv4 ? ' + (net.isIP('1234') === 4));
console.log('Это IP-адрес в формате IPv6 ? ' + (net.isIP('1234') === 6));
console.log('Это IP-адрес IPv4 ? ' + net.isIPv4('127.0.0.1'));
console.log('Это IP-адрес IPv6 ? ' + net.isIPv6('2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d'));
net-server.js
var net = require('net');
var optionalOptions = {
allowHalfOpen: false,
pauseOnConnect: false
};
var server = new net.Server(optionalOptions); // синоним net.createServer();
server.on('connection', function (socket) {
console.log('Клиет подключился.');
console.log('Адрес сервера: ' + socket.localAddress + ':' + socket.localPort);
console.log('Адрес клиента: ' + socket.remoteAddress + ':' + socket.remotePort + ', family: ' + socket.remoteFamily);
server.getConnections(function (error, count) {console.log('Число подключенных к серверу клиентов: ' + count);});
var request;
var response;
request = response = socket;
request.setEncoding('utf8');
request.on('data', function (data) {console.log('Данные от клиента: ' + (Buffer.isBuffer(data) ? data.toString() : data));});
request.on('end', function () {console.log('Клиент передал серверу все данные.');});
// response.setNoDelay(); // Установить отправку данных сразу после каждого вызова метода write().
response.write('Привет клиенту от сервера.');
response.end('Пока клиенту от сервера.');
server.close(); // Закрыть сервер и завершить все соединения с клиентами.
});
server.on('close', function () {
console.log('Сервер закрылся. Все соединения с клиентами завершились.');
});
server.on('error', function (error) {
console.log('Произошла ошибка на сервере.');
if (error.code === 'EADDRINUSE') {
console.log('Адрес сервера "127.0.0.1:8080" всё ещё использует другой сервер. Попробуем создать наш сервер ещё раз через секунду');
setTimeout(function () {
server.close();
server.listen(8080, '127.0.0.1');
}, 1000);
} else {
server.close(); // Закрыть сервер и завершить все соединения с клиентами.
throw error;
}
});
server.on('listening', function () {
console.log('Сервер начал прослушивать подключения от клиентов после вызова метода server.listen().');
console.log('Адрес сервера: ' + server.address().address + ':' + server.address().port + ', family: ' + server.address().family);
});
server.listen(8080, '127.0.0.1');
if (server.listening) {
console.log('Сервер в данный момент прослушивает подключения от клиентов.');
} else {
console.log('Сервер в данный момент не прослушивает подключения от клиентов.');
}
/*
server.ref(); // Удерживать программу от завершения, если в ней остался работать только этот сервер.
server.unref(); // Убрать удержание программы от завершения, если в ней остался работать только этот сервер.
*/
net-client.js
var net = require('net');
var optionalOptions = {
allowHalfOpen: false,
pauseOnConnect: false,
readable: false,
writrable: false
};
var client = new net.Socket(optionalOptions); // синоним net.createConnection(optionalOptions);
client.setEncoding('utf8');
client.on('data', function (data) {console.log('Данные от сервера: ' + (Buffer.isBuffer(data) ? data.toString() : data));});
client.on('end', function () {console.log('Сервер передал клиенту все данные.');});
client.on('drain', function () {
console.log('Буфер записи пуст.');
});
client.setTimeout(1000);
client.on('timeout', function () {
console.log('Превышено время ожидания ответа от севера.');
});
client.on('close', function (hadError) {
console.log('Клиент закрыл свое соединение с сервером.');
if (hadError) {
console.log('Произошла ошибка во временя передачи данных.');
}
});
client.on('error', function (error) {
console.log('Произошла ошибка соедиения с сервером. Клиент сейчас автоматически выполнит метод client.close().');
});
client.on('ready', function () {
console.log('Был вызван метод client.connect(). Клиент готов к использованию.');
});
client.on('lookup', function (error, address, family, host) {
console.log('Поиск адреса подключения до создания соединения с сервером.');
console.log(address);
console.log(family);
console.log(host);
});
if (client.pending) {
console.log('Клиент ожидает вызова метод client.connect() и подключения к серверу.');
}
client.on('connect', function () {
console.log('Соединение с сервером установлено.');
});
// client.connect(8080, '127.0.0.1');
client.connect({
port: 8080, host: '127.0.0.1', // Адрес сервера
localPort: 9090, localAddress: '127.0.0.2', family: 4 // Локальный адрес клиента для передачи его серверу. IPv4.
});
if (client.connecting) {
console.log('В данный момент клиент устанавливает соединение с сервером.');
}
client.write('Привет серверу от клиента.');
client.end('Пока серверу от клиента.');
/*
client.destroy();
if (client.destroyed) {
console.log('Соединение с сервером уничтожено. Данные больше не могут быть отправлены.');
}
client.ref(); // Удерживать программу от завершения, если в ней остался работать только этот клиент.
client.unref(); // Убрать удержание программы от завершения, если в ней остался работать только этот клиент.
*/
var net = require('net');
console.log('Это IP-адрес ? ' + (net.isIP('1234') === 0 ? false : true));
console.log('Это IP-адрес в формате IPv4 ? ' + (net.isIP('1234') === 4));
console.log('Это IP-адрес в формате IPv6 ? ' + (net.isIP('1234') === 6));
console.log('Это IP-адрес IPv4 ? ' + net.isIPv4('127.0.0.1'));
console.log('Это IP-адрес IPv6 ? ' + net.isIPv6('2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d'));
net-server.js
var net = require('net');
var optionalOptions = {
allowHalfOpen: false,
pauseOnConnect: false
};
var server = new net.Server(optionalOptions); // синоним net.createServer();
server.on('connection', function (socket) {
console.log('Клиет подключился.');
console.log('Адрес сервера: ' + socket.localAddress + ':' + socket.localPort);
console.log('Адрес клиента: ' + socket.remoteAddress + ':' + socket.remotePort + ', family: ' + socket.remoteFamily);
server.getConnections(function (error, count) {console.log('Число подключенных к серверу клиентов: ' + count);});
var request;
var response;
request = response = socket;
request.setEncoding('utf8');
request.on('data', function (data) {console.log('Данные от клиента: ' + (Buffer.isBuffer(data) ? data.toString() : data));});
request.on('end', function () {console.log('Клиент передал серверу все данные.');});
// response.setNoDelay(); // Установить отправку данных сразу после каждого вызова метода write().
response.write('Привет клиенту от сервера.');
response.end('Пока клиенту от сервера.');
server.close(); // Закрыть сервер и завершить все соединения с клиентами.
});
server.on('close', function () {
console.log('Сервер закрылся. Все соединения с клиентами завершились.');
});
server.on('error', function (error) {
console.log('Произошла ошибка на сервере.');
if (error.code === 'EADDRINUSE') {
console.log('Адрес сервера "127.0.0.1:8080" всё ещё использует другой сервер. Попробуем создать наш сервер ещё раз через секунду');
setTimeout(function () {
server.close();
server.listen(8080, '127.0.0.1');
}, 1000);
} else {
server.close(); // Закрыть сервер и завершить все соединения с клиентами.
throw error;
}
});
server.on('listening', function () {
console.log('Сервер начал прослушивать подключения от клиентов после вызова метода server.listen().');
console.log('Адрес сервера: ' + server.address().address + ':' + server.address().port + ', family: ' + server.address().family);
});
server.listen(8080, '127.0.0.1');
if (server.listening) {
console.log('Сервер в данный момент прослушивает подключения от клиентов.');
} else {
console.log('Сервер в данный момент не прослушивает подключения от клиентов.');
}
/*
server.ref(); // Удерживать программу от завершения, если в ней остался работать только этот сервер.
server.unref(); // Убрать удержание программы от завершения, если в ней остался работать только этот сервер.
*/
net-client.js
var net = require('net');
var optionalOptions = {
allowHalfOpen: false,
pauseOnConnect: false,
readable: false,
writrable: false
};
var client = new net.Socket(optionalOptions); // синоним net.createConnection(optionalOptions);
client.setEncoding('utf8');
client.on('data', function (data) {console.log('Данные от сервера: ' + (Buffer.isBuffer(data) ? data.toString() : data));});
client.on('end', function () {console.log('Сервер передал клиенту все данные.');});
client.on('drain', function () {
console.log('Буфер записи пуст.');
});
client.setTimeout(1000);
client.on('timeout', function () {
console.log('Превышено время ожидания ответа от севера.');
});
client.on('close', function (hadError) {
console.log('Клиент закрыл свое соединение с сервером.');
if (hadError) {
console.log('Произошла ошибка во временя передачи данных.');
}
});
client.on('error', function (error) {
console.log('Произошла ошибка соедиения с сервером. Клиент сейчас автоматически выполнит метод client.close().');
});
client.on('ready', function () {
console.log('Был вызван метод client.connect(). Клиент готов к использованию.');
});
client.on('lookup', function (error, address, family, host) {
console.log('Поиск адреса подключения до создания соединения с сервером.');
console.log(address);
console.log(family);
console.log(host);
});
if (client.pending) {
console.log('Клиент ожидает вызова метод client.connect() и подключения к серверу.');
}
client.on('connect', function () {
console.log('Соединение с сервером установлено.');
});
// client.connect(8080, '127.0.0.1');
client.connect({
port: 8080, host: '127.0.0.1', // Адрес сервера
localPort: 9090, localAddress: '127.0.0.2', family: 4 // Локальный адрес клиента для передачи его серверу. IPv4.
});
if (client.connecting) {
console.log('В данный момент клиент устанавливает соединение с сервером.');
}
client.write('Привет серверу от клиента.');
client.end('Пока серверу от клиента.');
/*
client.destroy();
if (client.destroyed) {
console.log('Соединение с сервером уничтожено. Данные больше не могут быть отправлены.');
}
client.ref(); // Удерживать программу от завершения, если в ней остался работать только этот клиент.
client.unref(); // Убрать удержание программы от завершения, если в ней остался работать только этот клиент.
*/
Node.js - Process and Child Process
parent-process-spawn.js
var child_process = require('child_process');
// spawn() является универсальным методом. exec(), execFile(), fork() наследуют от него.
// {shell: true} - говорит о том, стоит ли выполнять команду терминале командной строки или файл можно запустить самостоятельно вне терминала.
var childProcess = child_process.spawn('dir', ['/b', '/o:-n'], {shell: true}); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-exec.js
var child_process = require('child_process');
// exec() открывет терминал командной строки и выполняет в нем переданную команду.
var childProcess = child_process.exec('dir /b /o:-n'); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-execFile.js
var child_process = require('child_process');
// execFile() полностью аналогичен exec() - открывет терминал командной строки и выполняет в нем переданную команду, но в отлчии от exec() не показывает сам терминал на экране.
// В Windows файлы .bat и .cmd не могут работать вне терминала, поэтому execFile() не сможет их запустить.
var childProcess = child_process.execFile('node.exe', ['--version']); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-fork.js
var child_process = require('child_process');
// fork() - запускает файл JavaScript-файл, находящийся по переданному адресу в отдельно мпроцессе Node.js и соединяет его с родительским процессом, который его создал, для обеспечения возможности обмена данными между ними с помощью сообщений.
var childProcess = child_process.fork('./child-process.js', ['name']);
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
childProcess.on('disconnect', function () {
console.log('Child process disconneted.');
});
childProcess.on('message', function (message) {
console.log('Message from child process: ' + message.text);
});
if (childProcess.connected) {
childProcess.send({text: 'Parent process message.'});
}
/*
childProcess.ref();
childProcess.unref();
*/
child-process.js
var child_process = require('child_process');
// spawn() является универсальным методом. exec(), execFile(), fork() наследуют от него.
// {shell: true} - говорит о том, стоит ли выполнять команду терминале командной строки или файл можно запустить самостоятельно вне терминала.
var childProcess = child_process.spawn('dir', ['/b', '/o:-n'], {shell: true}); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-exec.js
var child_process = require('child_process');
// exec() открывет терминал командной строки и выполняет в нем переданную команду.
var childProcess = child_process.exec('dir /b /o:-n'); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-execFile.js
var child_process = require('child_process');
// execFile() полностью аналогичен exec() - открывет терминал командной строки и выполняет в нем переданную команду, но в отлчии от exec() не показывает сам терминал на экране.
// В Windows файлы .bat и .cmd не могут работать вне терминала, поэтому execFile() не сможет их запустить.
var childProcess = child_process.execFile('node.exe', ['--version']); // Поключается к stdin, stdout, stderr созданного дочернего процесса и получает данные от него.
childProcess.stdout.on('data', function (data) {
console.log('STDOUT from child process: ' + data);
});
childProcess.stderr.on('data', function (data) {
console.log('STDERR from child process: ' + data);
});
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
/*
childProcess.ref();
childProcess.unref();
*/
parent-process-fork.js
var child_process = require('child_process');
// fork() - запускает файл JavaScript-файл, находящийся по переданному адресу в отдельно мпроцессе Node.js и соединяет его с родительским процессом, который его создал, для обеспечения возможности обмена данными между ними с помощью сообщений.
var childProcess = child_process.fork('./child-process.js', ['name']);
childProcess.on('error', function (error) {
console.log('Child process error.');
console.log(error);
});
childProcess.on('close', function (code, signal) {
console.log('Child process exited with code ' + code + ' and signal ' + signal);
});
childProcess.on('disconnect', function () {
console.log('Child process disconneted.');
});
childProcess.on('message', function (message) {
console.log('Message from child process: ' + message.text);
});
if (childProcess.connected) {
childProcess.send({text: 'Parent process message.'});
}
/*
childProcess.ref();
childProcess.unref();
*/
child-process.js
process.on('message', function (message) {
console.log('Message from parent process: ' + message.text);
});
process.on('disconnect', function () {
console.log('Process disconneted.');
});
if (process.connected) {
process.send({text: 'Child process message.'});
setTimeout (function () {
process.disconnect();
}, 1000);
}
Node.js - Streams Readable / Writable
Node.js Stream Readable
var Readable = require('stream').Readable;
var rs = new Readable();
rs.push('beep ');
rs.push('boop');
rs.push(null);
rs.pipe(process.stdout);
var c = 97;
rs._read = function () {
if (c >= 'z'.charCodeAt(0)) {
return rs.push(null);
}
setTimeout(function () {
rs.push(String.fromCharCode(++c));
}, 100);
};
rs.on('data', function (data) {console.log(data.toString());});
rs.on('end', function (data) {console.log('END');});
Node.js Stream Writable
var Writable = require('stream').Writable;
var ws = Writable();
ws._write = function (chunk, enc, next) {
console.log(chunk.toString());
next();
};
process.stdin.pipe(ws);
var fs = require('fs');
var ws = fs.createWriteStream('./message.txt');
ws.write('beep ');
setTimeout(function () {
ws.end('boop');
}, 1000);
Examples
// ------------------------------------------------- //
var fs = require('fs');
var readStream = fs.createReadStream('./big-file.txt');
// Read stream, the "data" way
readStream.on('data', function (chunk) {
// do something with chunk
});
readStream.on('end', function () {
// stream finished
});
// ------------------------------------------------- //
// Read stream, the "readable" way
readStream.on('readable', function () {
var chunk;
while ((chunk = readStream.read()) !== null) {
// do something with chunk
}
});
readStream.on('end', function () {
// stream finished
});
// ------------------------------------------------- //
var http = require('http');
var fs = require('fs');
var server = http.createServer();
server.on('request', function (request, response) {
fs.createReadStream('./big-file.txt').pipe(response);
});
server.listen(8000, '127.0.0.1');
// ------------------------------------------------- //
var http = require('http');
var fs = require('fs');
var stream = require('stream');
var server = http.createServer();
server.on('request', function (request, response) {
// automatically deastroy response if there is an error
stream.pipeline(fs.createReadStream('./big-file.txt'), response, function (error) {
if (error) {console.error(error);}
});
});
server.listen(8000, '127.0.0.1');
var Readable = require('stream').Readable;
var rs = new Readable();
rs.push('beep ');
rs.push('boop');
rs.push(null);
rs.pipe(process.stdout);
var c = 97;
rs._read = function () {
if (c >= 'z'.charCodeAt(0)) {
return rs.push(null);
}
setTimeout(function () {
rs.push(String.fromCharCode(++c));
}, 100);
};
rs.on('data', function (data) {console.log(data.toString());});
rs.on('end', function (data) {console.log('END');});
Node.js Stream Writable
var Writable = require('stream').Writable;
var ws = Writable();
ws._write = function (chunk, enc, next) {
console.log(chunk.toString());
next();
};
process.stdin.pipe(ws);
var fs = require('fs');
var ws = fs.createWriteStream('./message.txt');
ws.write('beep ');
setTimeout(function () {
ws.end('boop');
}, 1000);
Examples
// ------------------------------------------------- //
var fs = require('fs');
var readStream = fs.createReadStream('./big-file.txt');
// Read stream, the "data" way
readStream.on('data', function (chunk) {
// do something with chunk
});
readStream.on('end', function () {
// stream finished
});
// ------------------------------------------------- //
// Read stream, the "readable" way
readStream.on('readable', function () {
var chunk;
while ((chunk = readStream.read()) !== null) {
// do something with chunk
}
});
readStream.on('end', function () {
// stream finished
});
// ------------------------------------------------- //
var http = require('http');
var fs = require('fs');
var server = http.createServer();
server.on('request', function (request, response) {
fs.createReadStream('./big-file.txt').pipe(response);
});
server.listen(8000, '127.0.0.1');
// ------------------------------------------------- //
var http = require('http');
var fs = require('fs');
var stream = require('stream');
var server = http.createServer();
server.on('request', function (request, response) {
// automatically deastroy response if there is an error
stream.pipeline(fs.createReadStream('./big-file.txt'), response, function (error) {
if (error) {console.error(error);}
});
});
server.listen(8000, '127.0.0.1');
понедельник, 25 марта 2019 г.
Streams, Promises, Event Listeners, Event Listeners
Streams
stream.on('data', data => {
console.log(data)
})
stream.on('end', () => {
console.log("Finished")
})
stream.on('error', err => {
console.error(err)
})
Promises
somePromise()
.then(data => console.log(data))
.catch(err => console.error(err))
Event Listeners
document.addEventListener('click', event => {
console.log(event.clientX)
})
Rx
const observable = {
subscribe: observer => {
},
pipe: operator => {
},
}
const observer = {
next: x => {
console.log(x)
},
error: err => {
console.log(err)
},
complete: () => {
console.log("done")
}
}
observable
.pipe(map(e => e.clientX))
.pipe(map(x => x - 1000))
.subscribe(observer)
--------------------------------------
Rx Map Operator
const observable = {
subscribe: observer => {
document.addEventListener("click", event => {
observer.next(event.clientX)
})
},
pipe: operator => {
return operator(this)
}
}
const observer = {
next: x => {
console.log(x)
},
error: err => {
console.log(err)
},
complete: () => {
console.log("done")
}
}
const map = f => {
return observable => {
subscribe: observer => {
observable.subscribe({
next: x => {
observer.next(f(x))
},
error: err => {
console.error(err)
},
complete: () => {
console.log("finished")
}
})
},
pipe: operator => {
return operator(this)
},
}
}
observable
.pipe(map(e => e.clientX))
.pipe(map(x => x - 1000))
.subscribe(observer)
stream.on('data', data => {
console.log(data)
})
stream.on('end', () => {
console.log("Finished")
})
stream.on('error', err => {
console.error(err)
})
Promises
somePromise()
.then(data => console.log(data))
.catch(err => console.error(err))
Event Listeners
document.addEventListener('click', event => {
console.log(event.clientX)
})
Rx
const observable = {
subscribe: observer => {
},
pipe: operator => {
},
}
const observer = {
next: x => {
console.log(x)
},
error: err => {
console.log(err)
},
complete: () => {
console.log("done")
}
}
observable
.pipe(map(e => e.clientX))
.pipe(map(x => x - 1000))
.subscribe(observer)
--------------------------------------
Rx Map Operator
const observable = {
subscribe: observer => {
document.addEventListener("click", event => {
observer.next(event.clientX)
})
},
pipe: operator => {
return operator(this)
}
}
const observer = {
next: x => {
console.log(x)
},
error: err => {
console.log(err)
},
complete: () => {
console.log("done")
}
}
const map = f => {
return observable => {
subscribe: observer => {
observable.subscribe({
next: x => {
observer.next(f(x))
},
error: err => {
console.error(err)
},
complete: () => {
console.log("finished")
}
})
},
pipe: operator => {
return operator(this)
},
}
}
observable
.pipe(map(e => e.clientX))
.pipe(map(x => x - 1000))
.subscribe(observer)
пятница, 8 февраля 2019 г.
Redux.js - простые примеры устройства.
// Устройство функции createStore()
const defaultState = {
counter: 0
};
function createUpAction (value) {
return {
type: 'UP',
value: value
};
}
function counterReducer (state = defaultState, action) {
const stateCopy = lodash.cloneDeep(state);
switch (action.type) {
case 'UP':
stateCopy.counter += action.value;
return stateCopy;
case 'DOWN':
stateCopy.counter -= action.value
return stateCopy;
default:
return state;
}
}
const store = createStore(counterReducer);
store.dispatch(createUpAction(1));
store.getState(); // {counter: 1}
function createStore (reducer, initialState) {
let state = initialState;
return {
dispatch: (action) => {state = reducer(state, action);},
getState: () => {return state;}
}
}
Устройство функции combineReducers()
const reducer = combineReducers({
todoState: todoReducer,
counterState: counterReducer
})
function counterReducer (state, action) {
if (action.type === 'ADD') {
return state + 1;
} else {
return state;
}
}
const initialState = {
todoState: [],
counterState: 0,
}
const store = createStore(reducer, initialState);
function combineReducers (reducersMap) {
return function combinationReducer (state, action) {
const nextState = {};
Object.entries(reducersMap).forEach(([key, reducer]) => {
nextState[key] = reducer(state[key], action);
});
return nextState;
}
}
Устройство функции applyMiddleware()
const createStoreWithMiddleware = applyMiddleware(someMiddleware)(createStore);
const store = createStoreWithMiddleware(reducer, initialState);
const thunk = (store) => (dispatch) => (action) => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return dispatch(action);
}
function createStore (reducer, initialState) {
let state = initialState;
return {
dispatch: (action) => {state = reducer(state, action);},
getState: () => {return state;}
}
}
function applyMiddleware (middleware) {
return function createStoreWithMiddleware (createStore) {
return (reducer, state) => {
const store = createStore(reducer, state);
return {
dispatch: action => middleware(store)(store.dispatch)(action),
getState: store.getState
}
}
}
}
const defaultState = {
counter: 0
};
function createUpAction (value) {
return {
type: 'UP',
value: value
};
}
function counterReducer (state = defaultState, action) {
const stateCopy = lodash.cloneDeep(state);
switch (action.type) {
case 'UP':
stateCopy.counter += action.value;
return stateCopy;
case 'DOWN':
stateCopy.counter -= action.value
return stateCopy;
default:
return state;
}
}
const store = createStore(counterReducer);
store.dispatch(createUpAction(1));
store.getState(); // {counter: 1}
function createStore (reducer, initialState) {
let state = initialState;
return {
dispatch: (action) => {state = reducer(state, action);},
getState: () => {return state;}
}
}
Устройство функции combineReducers()
const reducer = combineReducers({
todoState: todoReducer,
counterState: counterReducer
})
function counterReducer (state, action) {
if (action.type === 'ADD') {
return state + 1;
} else {
return state;
}
}
const initialState = {
todoState: [],
counterState: 0,
}
const store = createStore(reducer, initialState);
function combineReducers (reducersMap) {
return function combinationReducer (state, action) {
const nextState = {};
Object.entries(reducersMap).forEach(([key, reducer]) => {
nextState[key] = reducer(state[key], action);
});
return nextState;
}
}
Устройство функции applyMiddleware()
const createStoreWithMiddleware = applyMiddleware(someMiddleware)(createStore);
const store = createStoreWithMiddleware(reducer, initialState);
const thunk = (store) => (dispatch) => (action) => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return dispatch(action);
}
function createStore (reducer, initialState) {
let state = initialState;
return {
dispatch: (action) => {state = reducer(state, action);},
getState: () => {return state;}
}
}
function applyMiddleware (middleware) {
return function createStoreWithMiddleware (createStore) {
return (reducer, state) => {
const store = createStore(reducer, state);
return {
dispatch: action => middleware(store)(store.dispatch)(action),
getState: store.getState
}
}
}
}
Rx.js - простые примеры.
Примеры работы библиотеки Rx.
Пример заполнения потока данными.
// Обработчик данных в потоке.
const observer = {
next: value => console.log(value), // 1, 2
error: error => console.error(error), //
complete: () => console.log("completed") // completed
};
// Заполнитель потока данными.
const observable = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.complete();
})
// Связывания обработчика данных с заполнителем потока данными.
observable.subscribe(observer);
Пример заполнения потока данными и отписки от потока.
// Обработчик данных в потоке.
const observer = {
next: value => console.log(value), // 0, 1, 2, 3, 4
};
// Заполнитель потока данными, возвращающий функцию отписки, вызов которой останавливает добавление данных в поток данных.
const timer = new Observable(observer => {
let counter = 0; //объявляем счетчик
const intervalId = setInterval(() => {
observer.next(counter++); // передаем значение счетчика наблюдателю и увеличиваем его на единицу
}, 1000);
return () => {
clearInterval(intervalId);
}
});
// Связывания обработчика данных с заполнителем потока данными с возвратом функции отписки, вызов которой остановит добавление данных в поток данных.
const timerSubscription = timer.subscribe(observer);
// Остановка через некоторое время заполнение потока данными с помощью функции отписки.
setTimeout(() => timerSubscription.unsubscribe(), 5000); // поток завершиться через 5 секунд из-за выполненного clearInterval(intervalId)
Пример заполнения потока данными.
// Обработчик данных в потоке.
const observer = {
next: value => console.log(value), // 1, 2
error: error => console.error(error), //
complete: () => console.log("completed") // completed
};
// Заполнитель потока данными.
const observable = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.complete();
})
// Связывания обработчика данных с заполнителем потока данными.
observable.subscribe(observer);
Пример заполнения потока данными и отписки от потока.
// Обработчик данных в потоке.
const observer = {
next: value => console.log(value), // 0, 1, 2, 3, 4
};
// Заполнитель потока данными, возвращающий функцию отписки, вызов которой останавливает добавление данных в поток данных.
const timer = new Observable(observer => {
let counter = 0; //объявляем счетчик
const intervalId = setInterval(() => {
observer.next(counter++); // передаем значение счетчика наблюдателю и увеличиваем его на единицу
}, 1000);
return () => {
clearInterval(intervalId);
}
});
// Связывания обработчика данных с заполнителем потока данными с возвратом функции отписки, вызов которой остановит добавление данных в поток данных.
const timerSubscription = timer.subscribe(observer);
// Остановка через некоторое время заполнение потока данными с помощью функции отписки.
setTimeout(() => timerSubscription.unsubscribe(), 5000); // поток завершиться через 5 секунд из-за выполненного clearInterval(intervalId)
Подписаться на:
Сообщения (Atom)