From 5189ac8f316896dfe6651b4a096a1d868ccb5b10 Mon Sep 17 00:00:00 2001 From: RomanHasiuk Date: Tue, 10 Feb 2026 16:13:56 +0200 Subject: [PATCH 1/4] Solution --- src/createServer.js | 89 ++++++++++++++++++++++++++++++++++++++++++++- src/index.html | 26 +++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/index.html diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..f813d0c 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,93 @@ 'use strict'; +const http = require('http'); +const zlib = require('zlib'); +const { pipeline } = require('node:stream'); +const fs = require('fs'); +const path = require('path'); +const formidable = require('formidable'); + +function handleGetIndex(res) { + const filePath = path.join(__dirname, 'index.html'); + + res.writeHead(200, { 'Content-Type': 'text/html' }); + fs.createReadStream(filePath).pipe(res); +} + +function handleCompress(req, res) { + const form = new formidable.IncomingForm(); + + form.parse(req, (err, fields, files) => { + if (err) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Invalid form data'); + + return; + } + + const compressionType = Array.isArray(fields.compressionType) + ? fields.compressionType[0] + : fields.compressionType; + + const file = Array.isArray(files.file) ? files.file[0] : files.file; + + if (!file || !compressionType) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Missing file or compression type'); + + return; + } + + const compressors = { + gzip: { ext: '.gzip', stream: zlib.createGzip }, + deflate: { ext: '.deflate', stream: zlib.createDeflate }, + br: { ext: '.br', stream: zlib.createBrotliCompress }, + }; + + const config = compressors[compressionType]; + + if (!config) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Unsupported compression type'); + + return; + } + + res.writeHead(200, { + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename=${file.originalFilename}${config.ext}`, + }); + + pipeline( + fs.createReadStream(file.filepath), + config.stream(), + res, + () => {}, + ); + }); +} + function createServer() { - /* Write your code here */ - // Return instance of http.Server class + return http.createServer((req, res) => { + const { method, url } = req; + + if (method === 'GET' && (url === '/' || url === '/index.html')) { + return handleGetIndex(res); + } + + if (method === 'GET' && url === '/compress') { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + + return res.end('GET not allowed'); + } + + if (method === 'POST' && url === '/compress') { + return handleCompress(req, res); + } + + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('Not found'); + }); } module.exports = { diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..d9214a5 --- /dev/null +++ b/src/index.html @@ -0,0 +1,26 @@ + + + + + + Compression App + + +

File Compression

+
+
+ + +
+
+ + +
+ +
+ + \ No newline at end of file From 6a0f37a6847d5da29c4812fe4113bcf99550b716 Mon Sep 17 00:00:00 2001 From: RomanHasiuk Date: Tue, 10 Feb 2026 17:04:33 +0200 Subject: [PATCH 2/4] extension mapping for tests and pipeline callback --- src/createServer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index f813d0c..7440978 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -39,8 +39,8 @@ function handleCompress(req, res) { } const compressors = { - gzip: { ext: '.gzip', stream: zlib.createGzip }, - deflate: { ext: '.deflate', stream: zlib.createDeflate }, + gzip: { ext: '.gz', stream: zlib.createGzip }, + deflate: { ext: '.dfl', stream: zlib.createDeflate }, br: { ext: '.br', stream: zlib.createBrotliCompress }, }; @@ -55,7 +55,7 @@ function handleCompress(req, res) { res.writeHead(200, { 'Content-Type': 'application/octet-stream', - 'Content-Disposition': `attachment; filename=${file.originalFilename}${config.ext}`, + 'Content-Disposition': `attachment; filename=${file.originalFilename}.${compressionType}`, }); pipeline( From ad09dce509681ba27fb85c7d130bc5d517bdc31b Mon Sep 17 00:00:00 2001 From: RomanHasiuk Date: Tue, 10 Feb 2026 17:21:46 +0200 Subject: [PATCH 3/4] corrected pipeline callback --- src/createServer.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/createServer.js b/src/createServer.js index 7440978..7c9e177 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -62,7 +62,15 @@ function handleCompress(req, res) { fs.createReadStream(file.filepath), config.stream(), res, - () => {}, + (error) => { + if (error) { + if (!res.headersSent) { + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end('Internal Server Error'); + } + res.destroy(); + } + }, ); }); } From be1b4cc1238aa5f2332ee70f2b1d22343ed72bd0 Mon Sep 17 00:00:00 2001 From: RomanHasiuk Date: Tue, 10 Feb 2026 18:43:46 +0200 Subject: [PATCH 4/4] Solution #2 --- src/createServer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 7c9e177..4a5909b 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -39,8 +39,8 @@ function handleCompress(req, res) { } const compressors = { - gzip: { ext: '.gz', stream: zlib.createGzip }, - deflate: { ext: '.dfl', stream: zlib.createDeflate }, + gzip: { ext: '.gzip', stream: zlib.createGzip }, + deflate: { ext: '.deflate', stream: zlib.createDeflate }, br: { ext: '.br', stream: zlib.createBrotliCompress }, }; @@ -55,7 +55,7 @@ function handleCompress(req, res) { res.writeHead(200, { 'Content-Type': 'application/octet-stream', - 'Content-Disposition': `attachment; filename=${file.originalFilename}.${compressionType}`, + 'Content-Disposition': `attachment; filename=${file.originalFilename}${config.ext}`, }); pipeline(