From a9e40de231d116631d04b87f040a8c4097c1a858 Mon Sep 17 00:00:00 2001 From: Serhii Dmytruk Date: Fri, 27 Mar 2026 15:26:24 +0200 Subject: [PATCH 1/4] Solution --- index.html | 20 +++++++++ package.json | 2 +- src/createServer.js | 102 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..57ce827 --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + + + Document + + +
+ + + + +
+ + diff --git a/package.json b/package.json index 1d03d64..8e6392d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^2.1.3", "axios": "^1.7.2", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..063d266 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,106 @@ +/* eslint-disable no-console */ 'use strict'; +const http = require('node:http'); +const fs = require('node:fs'); +const path = require('node:path'); +const mime = require('mime-types'); +const zlib = require('node:zlib'); +const { formidable } = require('formidable'); + function createServer() { - /* Write your code here */ - // Return instance of http.Server class + return http.createServer((req, res) => { + const url = new URL(req.url || '', `http://${req.headers.host}`); + const requestPath = url.pathname.slice(1) || 'index.html'; + const realPath = requestPath; + + const compressionMap = { + gzip: () => zlib.createGzip(), + deflate: () => zlib.createDeflate(), + br: () => zlib.createBrotliCompress(), + }; + + if (req.url === '/compress' && req.method === 'POST') { + const form = formidable({ multiples: false }); + + form.parse(req, (err, fields, files) => { + if (err) { + res.statusCode = 400; + res.end('Invalid form'); + + 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.statusCode = 400; + res.end('Invalid form'); + + return; + } + + const createCompressionStream = compressionMap[compressionType]; + + if (!createCompressionStream) { + res.statusCode = 400; + res.end('Unsupported compression type'); + + return; + } + + const readStream = fs.createReadStream(file.filepath); + const compressStream = createCompressionStream(); + const outputFileName = `${file.originalFilename}.${compressionType}`; + + res.statusCode = 200; + + res.setHeader( + 'Content-Disposition', + `attachment; filename=${outputFileName}`, + ); + + readStream.on('error', () => { + res.statusCode = 500; + res.end('Server error'); + }); + + compressStream.on('error', () => { + res.statusCode = 500; + res.end('Server error'); + }); + + readStream.pipe(compressStream).pipe(res); + }); + + return; + } + + if (req.url === '/compress' && req.method === 'GET') { + res.statusCode = 400; + res.end('Wrong URL'); + + return; + } + + if (req.url === '/' && req.method === 'GET') { + const fileStream = fs.createReadStream(realPath); + const mimeType = mime.contentType(path.extname(realPath)) || 'text/plain'; + + res.statusCode = 200; + res.setHeader('Content-Type', mimeType); + fileStream.pipe(res); + } else { + if (!fs.existsSync(realPath)) { + res.statusCode = 404; + res.end('Not Found'); + } + } + }); } module.exports = { From 3bb9413e500ade7c263ab0500a0607adba0bd881 Mon Sep 17 00:00:00 2001 From: Serhii Dmytruk Date: Fri, 27 Mar 2026 15:36:03 +0200 Subject: [PATCH 2/4] Solution --- src/createServer.js | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 063d266..80192dc 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -15,12 +15,21 @@ function createServer() { const realPath = requestPath; const compressionMap = { - gzip: () => zlib.createGzip(), - deflate: () => zlib.createDeflate(), - br: () => zlib.createBrotliCompress(), + gzip: { + extension: 'gzip', + createStream: () => zlib.createGzip(), + }, + deflate: { + extension: 'deflate', + createStream: () => zlib.createDeflate(), + }, + br: { + extension: 'br', + createStream: () => zlib.createBrotliCompress(), + }, }; - if (req.url === '/compress' && req.method === 'POST') { + if (url.pathname === '/compress' && req.method === 'POST') { const form = formidable({ multiples: false }); form.parse(req, (err, fields, files) => { @@ -44,9 +53,9 @@ function createServer() { return; } - const createCompressionStream = compressionMap[compressionType]; + const compressionConfig = compressionMap[compressionType]; - if (!createCompressionStream) { + if (!compressionConfig) { res.statusCode = 400; res.end('Unsupported compression type'); @@ -54,8 +63,8 @@ function createServer() { } const readStream = fs.createReadStream(file.filepath); - const compressStream = createCompressionStream(); - const outputFileName = `${file.originalFilename}.${compressionType}`; + const compressStream = compressionConfig.createStream(); + const outputFileName = `${file.originalFilename}.${compressionConfig.extension}`; res.statusCode = 200; @@ -80,26 +89,26 @@ function createServer() { return; } - if (req.url === '/compress' && req.method === 'GET') { + if (url.pathname === '/compress' && req.method === 'GET') { res.statusCode = 400; res.end('Wrong URL'); return; } - if (req.url === '/' && req.method === 'GET') { + if (url.pathname === '/' && req.method === 'GET') { const fileStream = fs.createReadStream(realPath); const mimeType = mime.contentType(path.extname(realPath)) || 'text/plain'; res.statusCode = 200; res.setHeader('Content-Type', mimeType); fileStream.pipe(res); - } else { - if (!fs.existsSync(realPath)) { - res.statusCode = 404; - res.end('Not Found'); - } + + return; } + + res.statusCode = 404; + res.end('Not Found'); }); } From c7d203702c1cb68a43a581c98095290ba171cc1c Mon Sep 17 00:00:00 2001 From: Serhii Dmytruk Date: Fri, 27 Mar 2026 15:52:56 +0200 Subject: [PATCH 3/4] Fix test --- tests/createServer.test.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/createServer.test.js b/tests/createServer.test.js index b53ef15..27b2977 100644 --- a/tests/createServer.test.js +++ b/tests/createServer.test.js @@ -29,12 +29,15 @@ function stringToStream(str) { const compressionTypes = { gzip: { decompress: util.promisify(zlib.gunzip), + extension: 'gz', }, deflate: { decompress: util.promisify(zlib.inflate), + extension: 'dfl', }, br: { decompress: util.promisify(zlib.brotliDecompress), + extension: 'br', }, }; @@ -96,7 +99,7 @@ describe('createServer', () => { }); Object.entries(compressionTypes).forEach( - ([compressionType, { decompress }]) => { + ([compressionType, { decompress, extension }]) => { describe(`compression type "${compressionType}"`, () => { it('should respond with 200 status code', () => { expect.assertions(1); @@ -124,7 +127,7 @@ describe('createServer', () => { headers: formData.getHeaders(), }) .then((res) => { - const expectedHeader = `attachment; filename=${filename}.${compressionType}`; + const expectedHeader = `attachment; filename=${filename}.${extension}`; expect(res.headers['content-disposition']).toBe( expectedHeader, From 74e1ad04cbde2041fb56f910780de9b61520d955 Mon Sep 17 00:00:00 2001 From: Serhii Dmytruk Date: Fri, 27 Mar 2026 15:58:06 +0200 Subject: [PATCH 4/4] Fix --- src/createServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 80192dc..8fba7a5 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -16,11 +16,11 @@ function createServer() { const compressionMap = { gzip: { - extension: 'gzip', + extension: 'gz', createStream: () => zlib.createGzip(), }, deflate: { - extension: 'deflate', + extension: 'dfl', createStream: () => zlib.createDeflate(), }, br: {