четверг, 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('Отправка данных на сервер завершена.');}

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(); // Убрать удержание программы от завершения, если в ней остался работать только этот клиент.
*/

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

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');