HTTP2 moduli
HTTP/2 nima?
Node.js HTTP/2 moduli HTTP/2 protokolini amalga oshirishni ta’minlaydi, u yaxshilangan ishlash, serverni surish imkoniyatlari, sarlavhalarni siqish va bitta ulanish orqali multiplekslashni taklif qiladi.
HTTP/2 bir nechta asosiy xususiyatlarga ega HTTP/1.1 ni yaxshilaydi:
- Ikkilik protokoli: HTTP/2 ma’lumotlarni uzatish uchun HTTP/1.1 matn formatidan ko‘ra ikkilik formatdan foydalanadi, bu uni tahlil qilishni samaraliroq qiladi.
- Multiplexing: Bir vaqtning o‘zida bir nechta so‘rovlar va javoblar bitta ulanish orqali yuborilishi mumkin.
- Sarlavhani siqish: HTTP/2 yukni kamaytirish uchun sarlavhalarni siqib chiqaradi.
- Server push: Serverlar mijozlar so‘rashidan oldin resurslarni faol ravishda yuborishlari mumkin.
- Stream ustuvorligi: Resurslar turli ustuvorliklar bilan yetkazib berilishi mumkin.
HTTP/2 modulidan foydalanish
Node.js da HTTP/2 moduliga quyidagilar yordamida kirish mumkin:
const http2 = require('http2');
HTTP/2 moduli Node.js v10.0.0 dan boshlab barqaror. Shuni ta’kidlash kerakki, HTTP/2 ko‘pgina brauzerlarda xavfsiz ulanishni (HTTPS) talab qiladi, shuning uchun ko‘pchilik misollarda TLS/SSL ishlatiladi.
HTTP/2 serverini yaratish
TLS yordamida asosiy HTTP/2 serverini yaratish misoli:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
// Read the TLS certificate and key
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
};
// Create an HTTP/2 server
const server = http2.createSecureServer(options);
// Handle stream events
server.on('stream', (stream, headers) => {
// Get the path from headers
const path = headers[':path'];
// Send a response
if (path === '/') {
stream.respond({
'content-type': 'text/html',
':status': 200
});
stream.end('<h1>Hello from HTTP/2!</h1>');
} else {
stream.respond({
':status': 404
});
stream.end('Not found');
}
});
// Start the server
const port = 8080;
server.listen(port, () => {
console.log(`HTTP/2 server running at https://localhost:${port}`);
});
Ushbu misol sizda TLS sertifikat fayllari mavjudligini nazarda tutadi. Rivojlanish uchun siz OpenSSL yordamida o‘z-o‘zidan imzolangan sertifikatlarni yaratishingiz mumkin. Ishlab chiqarish uchun ishonchli sertifikat organidan foydalaning.
Shuningdek, siz HTTP/2 serverini TLSsiz yaratishingiz mumkin (shifrlashsiz to‘g‘ridan-to‘g‘ri HTTP/2 ulanishlari uchun):
const http2 = require('http2');
// Create an HTTP/2 server without TLS
const server = http2.createServer();
server.on('stream', (stream, headers) => {
stream.respond({
'content-type': 'text/html',
':status': 200
});
stream.end('<h1>Hello from HTTP/2 without TLS!</h1>');
});
server.listen(8080);
Aksariyat zamonaviy brauzerlar faqat TLS orqali HTTP/2 ni qo‘llab-quvvatlaydi, shuning uchun xavfsiz bo‘lmagan HTTP/2 serveri odatda faqat HTTP/2 aniq matnni qo‘llab-quvvatlaydigan maxsus HTTP/2 mijozlari bilan ishlaydi.
HTTP/2 mijozi
HTTP/2 serveriga ulanish uchun HTTP/2 mijozini yaratish:
const http2 = require('http2');
// Create a client
const client = http2.connect('https://localhost:8080', {
// For self-signed certificates in development
rejectUnauthorized: false
});
// Error handling
client.on('error', (err) => {
console.error('Client error:', err);
});
// Create a request
const req = client.request({ ':path': '/' });
// Handle response data
req.on('response', (headers) => {
console.log('Status:', headers[':status']);
console.log('Headers:', headers);
});
req.on('data', (chunk) => {
console.log('Received data:', chunk.toString());
});
req.on('end', () => {
console.log('Request completed');
client.close();
});
// Send the request
req.end();
HTTP/2 oqimlari
HTTP/2 mijoz va server o‘rtasidagi aloqa uchun oqimlardan foydalanadi. Har bir stream mijoz va server o‘rtasida almashinadigan mustaqil, ikki yo‘nalishli frameworklar ketma-ketligini ifodalaydi.
Oqimli voqealar
Muhim stream voqealariga quyidagilar kiradi:
'headers': Sarlavhalar qabul qilinganda chiqariladi'data': Ma’lumotlarning bir qismi qabul qilinganda chiqariladi'end': Stream tugagach chiqariladi'error': Xatolik yuz berganda chiqariladi
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
// Create a server
const server = http2.createSecureServer({
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
});
server.on('stream', (stream, headers) => {
// Handle stream events
stream.on('error', (error) => {
console.error('Stream error:', error);
});
stream.on('close', () => {
console.log('Stream closed');
});
// Handle request
stream.respond({
'content-type': 'text/plain',
':status': 200
});
// Send data in multiple chunks
stream.write('First chunk of data\n');
setTimeout(() => {
stream.write('Second chunk of data\n');
stream.end('Final chunk of data');
}, 1000);
});
server.listen(8080);
HTTP/2 Server Push
Serverni surish serverga resurslarni mijozga aniq so‘ralishidan oldin faol ravishda yuborish imkonini beradi. Bu ikki safarda kechikishlarni bartaraf etish orqali ishlashni yaxshilashi mumkin.
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
};
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
const requestPath = headers[':path'];
if (requestPath === '/') {
// Push CSS and JavaScript resources
stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
if (err) {
console.error('Error pushing stream:', err);
return;
}
pushStream.respond({
'content-type': 'text/css',
':status': 200
});
pushStream.end('body { color: blue; }');
});
stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
if (err) {
console.error('Error pushing stream:', err);
return;
}
pushStream.respond({
'content-type': 'application/javascript',
':status': 200
});
pushStream.end('console.log("Hello from HTTP/2 server push!");');
});
// Send the main HTML document
stream.respond({
'content-type': 'text/html',
':status': 200
});
stream.end(`
<!DOCTYPE html>
<html>
<head>
<title>HTTP/2 Server Push Example</title>
<link rel="stylesheet" href="/style.css">
<script src="/script.js"></script>
</head>
<body>
<h1>HTTP/2 Server Push Demo</h1>
<p>CSS and JavaScript were pushed by the server!</p>
</body>
</html>
`);
} else {
// Serve pushed resources if requested directly
if (requestPath === '/style.css') {
stream.respond({
'content-type': 'text/css',
':status': 200
});
stream.end('body { color: blue; }');
} else if (requestPath === '/script.js') {
stream.respond({
'content-type': 'application/javascript',
':status': 200
});
stream.end('console.log("Hello from HTTP/2 server push!");');
} else {
// Not found
stream.respond({ ':status': 404 });
stream.end('Not found');
}
}
});
server.listen(8080);
HTTP/2 sarlavhalari
HTTP/2 sarlavhalar uchun boshqa formatdan foydalanadi. Shunisi e’tiborga loyiqki, barcha sarlavhalar kichik harflar va so‘rovning soxta sarlavhalari ikki nuqta (:) bilan boshlanadi.
const http2 = require('http2');
// HTTP/2 pseudo-headers
const {
HTTP2_HEADER_METHOD,
HTTP2_HEADER_PATH,
HTTP2_HEADER_AUTHORITY,
HTTP2_HEADER_SCHEME,
HTTP2_HEADER_STATUS
} = http2.constants;
// Create a client
const client = http2.connect('https://localhost:8080', {
rejectUnauthorized: false
});
// Send a request with custom headers
const req = client.request({
[HTTP2_HEADER_METHOD]: 'GET',
[HTTP2_HEADER_PATH]: '/',
[HTTP2_HEADER_AUTHORITY]: 'localhost:8080',
[HTTP2_HEADER_SCHEME]: 'https',
'user-agent': 'node-http2/client',
'custom-header': 'custom-value'
});
req.on('response', (headers) => {
console.log('Response status:', headers[HTTP2_HEADER_STATUS]);
console.log('Response headers:', headers);
});
req.on('data', (chunk) => {
console.log('Received data:', chunk.toString());
});
req.on('end', () => {
client.close();
});
req.end();
HTTP/2 sozlamalari
HTTP/2 turli xil protokol sozlamalarini sozlash imkonini beradi:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt')),
// HTTP/2 settings
settings: {
// Max concurrent streams per connection
maxConcurrentStreams: 100,
// Initial window size for flow control
initialWindowSize: 1024 * 1024, // 1MB
// Enable server push
enablePush: true
}
};
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
stream.respond({
'content-type': 'text/html',
':status': 200
});
stream.end('<h1>HTTP/2 Server with Custom Settings</h1>');
});
server.listen(8080);
HTTP/1.1 bilan moslik
HTTP/2 serverlari uzluksiz yangilanish yo‘lini ta’minlovchi HTTP/1.1 so‘rovlarini ham bajarishi mumkin:
const http2 = require('http2');
const http = require('http');
const fs = require('fs');
const path = require('path');
// For HTTP/2 secure server
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt')),
allowHTTP1: true // Allow HTTP/1.1 connections
};
const server = http2.createSecureServer(options);
// Handler function for both HTTP/1.1 and HTTP/2
const handler = (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Hello from ${req.httpVersion} server!`);
};
// HTTP/1.1 compatibility request handler
server.on('request', handler);
// HTTP/2 specific stream handler
server.on('stream', (stream, headers) => {
stream.respond({
'content-type': 'text/plain',
':status': 200
});
stream.end(`Hello from HTTP/2 stream API!`);
});
server.listen(8080, () => {
console.log('Server running at https://localhost:8080/');
});
Ishlash bo‘yicha mulohazalar
HTTP/2 unumdorlikni yaxshilashni taklif qilsa-da, foydalanishni optimallashtirish muhim:
- Ulanishni qayta ishlatish - HTTP/2 bilan siz yangi ulanishlar yaratish o‘rniga bir nechta so‘rovlar uchun bitta ulanishdan foydalanishni maqsad qilishingiz kerak.
- Oqimlarni to‘g‘ri boshqarish - Streamlar kerak bo‘lmaganda ularni yopishni unutmang va bir vaqtda streamlar sonini kuzatib boring.
- Server Push strategiyasi - Faqat kerak bo‘lishi mumkin bo‘lgan resurslarni surish. Haddan tashqari surish tarmoqli kengligi va resurslarni behuda sarflashi mumkin.
- Sarlavhalarni siqish - Maxsus sarlavhalar soni va hajmini minimallashtirish orqali HTTP/2 sarlavhalarini siqish imkoniyatlaridan foydalaning.
HTTP/2 va HTTP/1.1
HTTP/2 va HTTP/1.1 o‘rtasidagi asosiy farqlar:
| Xususiyat | HTTP/1.1 | HTTP/2 |
|---|---|---|
| Protokol formati | Matnga asoslangan | Ikkilik asosli |
| Multiplekslash | No (requires multiple connections) | Ha (bitta ulanish orqali bir nechta oqim) |
| Sarlavhani siqish | Yo‘q | Yes (HPACK) |
| Server Push | Yo‘q | Ha |
| Stream nazorati | Asosiy | Kengaytirilgan, har bir stream |
| Ustuvorlik | Yo‘q | Ha |
Haqiqiy dunyo misoli: to‘liq veb-saytga xizmat ko‘rsatish
HTTP/2 bilan veb-saytga xizmat ko‘rsatishning to‘liq namunasi:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const mime = require('mime-types');
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
};
const server = http2.createSecureServer(options);
// Serve files from the public directory
const publicDir = path.join(__dirname, 'public');
server.on('stream', (stream, headers) => {
const reqPath = headers[':path'] === '/' ? '/index.html' : headers[':path'];
const filePath = path.join(publicDir, reqPath);
// Basic security check to prevent path traversal
if (!filePath.startsWith(publicDir)) {
stream.respond({ ':status': 403 });
stream.end('Forbidden');
return;
}
fs.stat(filePath, (err, stats) => {
if (err || !stats.isFile()) {
// File not found
stream.respond({ ':status': 404 });
stream.end('Not found');
return;
}
// Determine content type
const contentType = mime.lookup(filePath) || 'application/octet-stream';
// Serve the file
stream.respond({
'content-type': contentType,
':status': 200
});
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(stream);
fileStream.on('error', (err) => {
console.error('File stream error:', err);
stream.close(http2.constants.NGHTTP2_INTERNAL_ERROR);
});
});
});
server.listen(8080, () => {
console.log('HTTP/2 server running at https://localhost:8080/');
});
Ushbu misol mime-types paketini talab qiladi:
npm install mime-types
Kengaytirilgan stream boshqaruvi
HTTP/2 ning oqimlarni boshqarish imkoniyatlari bir vaqtning o‘zida bir nechta so‘rovlarni samarali boshqarish imkonini beradi. Mana, stream ustuvorligi va oqimni boshqarishni ko‘rsatadigan ilg‘or misol:
const http2 = require('http2');
const fs = require('fs');
// Create a server with custom settings
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
settings: {
initialWindowSize: 65535, // 64KB initial window
maxConcurrentStreams: 100,
enablePush: true
}
});
server.on('stream', (stream, headers) => {
// Get priority information
const weight = stream.priority && stream.priority.weight || 1;
const parent = stream.priority && stream.priority.parent ? 'with parent' : 'no parent';
console.log(`New stream ${stream.id} (weight: ${weight}, ${parent})`);
// Handle different priority levels
if (headers[':path'] === '/high-priority') {
stream.priority({ weight: 256, exclusive: true });
stream.respond({ ':status': 200, 'content-type': 'text/plain' });
stream.end('High priority content');
} else {
// Default priority
stream.respond({ ':status': 200, 'content-type': 'text/plain' });
stream.end('Standard priority content');
}
// Handle stream errors
stream.on('error', (error) => {
console.error(`Stream ${stream.id} error:`, error);
stream.end();
});
// Handle stream close
stream.on('close', () => {
console.log(`Stream ${stream.id} closed`);
});
});
server.listen(8443);
Xatolarni qayta ishlash va debug
Ishonchli HTTP/2 ilovalari uchun xatolarni to‘g‘ri hal qilish juda muhimdir. Xatolarni keng qamrovli hal qilishni qanday amalga oshirish mumkin:
const http2 = require('http2');
const fs = require('fs');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
async function startServer() {
try {
const [key, cert] = await Promise.all([
readFile('server.key'),
readFile('server.crt')
]);
const server = http2.createSecureServer({ key, cert });
// Global error handler
server.on('error', (err) => {
console.error('Server error:', err);
// Implement proper error recovery
});
// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
// Graceful shutdown
server.close(() => process.exit(1));
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
// Stream handler with error boundaries
server.on('stream', (stream, headers) => {
try {
// Simulate async operation
setTimeout(() => {
try {
if (Math.random() > 0.8) {
throw new Error('Random error for demonstration');
}
stream.respond({ ':status': 200 });
stream.end('Success!');
} catch (err) {
handleStreamError(stream, err);
}
}, 100);
} catch (err) {
handleStreamError(stream, err);
}
});
function handleStreamError(stream, error) {
console.error('Stream error:', error);
if (!stream.destroyed) {
stream.respond({
':status': 500,
'content-type': 'text/plain'
});
stream.end('Internal Server Error');
}
}
server.listen(8443, () => {
console.log('Server running on https://localhost:8443');
});
} catch (err) {
console.error('Failed to start server:', err);
process.exit(1);
}
}
startServer();
Ishlashni optimallashtirish
HTTP/2 ishlashini optimallashtirish uning o‘ziga xos xususiyatlarini tushunishni talab qiladi. Bu yerda asosiy strategiyalar:
1. Ulanishni birlashtirish
const http2 = require('http2');
const { URL } = require('url');
class HTTP2ConnectionPool {
constructor() {
this.connections = new Map();
}
async getConnection(url) {
const { origin } = new URL(url);
if (!this.connections.has(origin)) {
const client = http2.connect(origin, {
rejectUnauthorized: false // Only for development
});
// Handle connection errors
client.on('error', (err) => {
console.error('Connection error:', err);
this.connections.delete(origin);
});
// Remove connection when closed
client.on('close', () => {
this.connections.delete(origin);
});
this.connections.set(origin, {
client,
lastUsed: Date.now(),
inUse: 0
});
}
const conn = this.connections.get(origin);
conn.lastUsed = Date.now();
conn.inUse++;
return {
client: conn.client,
release: () => {
conn.inUse--;
}
};
}
// Clean up idle connections
startCleanup(interval = 30000) {
setInterval(() => {
const now = Date.now();
for (const [origin, conn] of this.connections.entries()) {
if (conn.inUse === 0 && (now - conn.lastUsed) > 60000) {
conn.client.destroy();
this.connections.delete(origin);
}
}
}, interval);
}
}
// Usage example
const pool = new HTTP2ConnectionPool();
pool.startCleanup();
async function makeRequest(url) {
const { client, release } = await pool.getConnection(url);
return new Promise((resolve, reject) => {
const req = client.request({ ':path': new URL(url).pathname });
let data = '';
req.on('response', (headers) => {
console.log('Status:', headers[':status']);
});
req.on('data', (chunk) => data += chunk);
req.on('end', () => {
release();
resolve(data);
});
req.on('error', (err) => {
release();
reject(err);
});
req.end();
});
}
2. Sarlavhani siqishni optimallashtirish
HTTP/2 sarlavhalar uchun HPACK siqishni ishlatadi. Optimallashtirish:
- Cookie o‘lchamlarini minimallashtirish
- Qisqa, ammo tavsiflovchi sarlavha nomlaridan foydalanish
- Ikki nusxadagi sarlavhalardan qochish
- Mumkin bo‘lganda HTTP/2-ga xos sarlavhalardan foydalanish
Xavfsizlik bo‘yicha eng yaxshi amaliyotlar
HTTP/2 dan foydalanayotganda quyidagi xavfsizlik amaliyotlariga amal qiling:
const http2 = require('http2');
const fs = require('fs');
const { createSecureContext } = require('tls');
// Security headers middleware
function securityHeaders(req, res, next) {
// Set security headers
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Content-Security-Policy', "default-src 'self'");
// Remove server header
res.removeHeader('X-Powered-By');
next();
}
// Create secure server with modern TLS settings
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
secureOptions:
require('constants').SSL_OP_NO_SSLv3 |
require('constants').SSL_OP_NO_TLSv1 |
require('constants').SSL_OP_NO_TLSv1_1,
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256'
].join(':'),
minVersion: 'TLSv1.3',
maxVersion: 'TLSv1.3',
// OCSP Stapling
requestCert: false,
rejectUnauthorized: true
};
const server = http2.createSecureServer(options);
// Apply security middleware
server.on('request', (req, res) => {
securityHeaders(req, res, () => {
// Request handling logic
res.setHeader('Content-Type', 'text/plain');
res.end('Secure HTTP/2 Response');
});
});
// Handle TLS errors
server.on('tlsClientError', (err, tlsSocket) => {
console.error('TLS Error:', err);
tlsSocket.destroy();
});
server.listen(8443);
Haqiqiy foydalanish holatlari
1. HTTP/2 bilan API shlyuzi
HTTP/2 bilan yuqori samarali API shlyuzini yaratish:
const http2 = require('http2');
const { URL } = require('url');
const path = require('path');
const fs = require('fs');
// Service registry
const services = {
'/users': 'http://users-service:3000',
'/products': 'http://products-service:3000',
'/orders': 'http://orders-service:3000'
};
// Create HTTP/2 server
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
});
// Route requests to appropriate services
server.on('stream', (stream, headers) => {
const path = headers[':path'];
const method = headers[':method'];
try {
// Find matching service
const servicePath = Object.keys(services).find(prefix =>
path.startsWith(prefix)
);
if (!servicePath) {
stream.respond({ ':status': 404 });
return stream.end('Not Found');
}
const targetUrl = new URL(path.slice(servicePath.length), services[servicePath]);
// Forward request to target service
const client = http2.connect(targetUrl.origin);
const req = client.request({
...headers,
':path': targetUrl.pathname + targetUrl.search,
':method': method,
':authority': targetUrl.host
});
// Pipe the response back to client
req.pipe(stream);
stream.pipe(req);
// Handle errors
req.on('error', (err) => {
console.error('Request error:', err);
if (!stream.destroyed) {
stream.respond({ ':status': 502 });
stream.end('Bad Gateway');
}
});
stream.on('error', (err) => {
console.error('Stream error:', err);
req.destroy();
});
} catch (err) {
console.error('Gateway error:', err);
if (!stream.destroyed) {
stream.respond({ ':status': 500 });
stream.end('Internal Server Error');
}
}
});
server.listen(443);
2. Real vaqt rejimida ma’lumotlarni uzatish
HTTP/2 bilan real vaqtda samarali ma’lumotlarni uzatish:
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
});
// In-memory storage for active streams
const streams = new Set();
// Broadcast data to all connected clients
function broadcast(data) {
const payload = JSON.stringify(data);
for (const stream of streams) {
try {
stream.write(`data: ${payload}\n\n`);
} catch (err) {
console.error('Stream write error:', err);
streams.delete(stream);
}
}
}
// Simulate data updates
setInterval(() => {
broadcast({
time: new Date().toISOString(),
value: Math.random() * 100
});
}, 1000);
server.on('stream', (stream, headers) => {
// Only handle GET requests
if (headers[':method'] !== 'GET') {
stream.respond({ ':status': 405 });
return stream.end();
}
// Set up Server-Sent Events headers
stream.respond({
'content-type': 'text/event-stream',
'cache-control': 'no-cache',
'connection': 'keep-alive',
':status': 200
});
// Add to active streams
streams.add(stream);
// Handle client disconnect
stream.on('close', () => {
streams.delete(stream);
});
// Send initial data
stream.write('event: connect\ndata: Connected\n\n');
});
server.listen(8443, () => {
console.log('HTTP/2 Server running on https://localhost:8443');
});
W3Schools Pathfinder
Yutuqlaringizni kuzating – bu bepul!
