From 3236051158df30fb329a09482c84d42321f2ce33 Mon Sep 17 00:00:00 2001 From: Anton Kuchmasov Date: Sun, 26 Oct 2025 14:05:24 +0200 Subject: [PATCH 1/3] solution --- .github/workflows/test.yml-template | 23 +++ package-lock.json | 99 ++++++--- package.json | 7 +- public/favicon.ico | Bin 0 -> 107781 bytes public/index.html | 32 +++ public/style.css | 24 +++ public/uploads/c8l5fhrccb1uk7kppkn7trrkm | 3 + src/createServer.js | 253 ++++++++++++++++++++++- 8 files changed, 410 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/test.yml-template create mode 100644 public/favicon.ico create mode 100644 public/index.html create mode 100644 public/style.css create mode 100644 public/uploads/c8l5fhrccb1uk7kppkn7trrkm diff --git a/.github/workflows/test.yml-template b/.github/workflows/test.yml-template new file mode 100644 index 0000000..bb13dfc --- /dev/null +++ b/.github/workflows/test.yml-template @@ -0,0 +1,23 @@ +name: Test + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/package-lock.json b/package-lock.json index d0b3b95..5551314 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,19 @@ "version": "1.0.0", "hasInstallScript": true, "license": "GPL-3.0", + "dependencies": { + "mime-types": "3.0.1" + }, "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "2.1.2", "axios": "^1.7.2", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", "eslint-plugin-node": "^11.1.0", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "3.5.4", "jest": "^29.7.0", "prettier": "^3.3.2" } @@ -1487,10 +1490,11 @@ } }, "node_modules/@mate-academy/scripts": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.8.6.tgz", - "integrity": "sha512-b4om/whj4G9emyi84ORE3FRZzCRwRIesr8tJHXa8EvJdOaAPDpzcJ8A0sFfMsWH9NUOVmOwkBtOXDu5eZZ00Ig==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.2.tgz", + "integrity": "sha512-gUXFdqqOfYzF9R3RSx2pCa5GLdOkxB9bFbF+dpUpzucdgGAANqOGdqpmNnMj+e3xA9YHraUWq3xo9cwe5vD9pQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", @@ -1516,6 +1520,19 @@ "eslint-scope": "5.1.1" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1885,6 +1902,16 @@ "@octokit/openapi-types": "^22.2.0" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -4269,16 +4296,43 @@ "node": ">= 6" } }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formidable": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", - "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, + "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -4624,15 +4678,6 @@ "node": ">= 0.4" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6969,21 +7014,21 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" diff --git a/package.json b/package.json index 1d03d64..9682a73 100644 --- a/package.json +++ b/package.json @@ -18,17 +18,20 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "2.1.2", "axios": "^1.7.2", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", "eslint-plugin-node": "^11.1.0", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "3.5.4", "jest": "^29.7.0", "prettier": "^3.3.2" }, "mateAcademy": { "projectType": "javascript" + }, + "dependencies": { + "mime-types": "3.0.1" } } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e353607d13cf7225b8546b1e95bc12866f421ffb GIT binary patch literal 107781 zcmd3Ngxb-GjaCdhol;TbaT4-@7R-7Wm-5m-Pio09UVx_pdySr1|-EV&Pet*Q5 zJo{|cCbP4%^PcydGXMYr5P<(aAb<)`lmY;C*l#HG|F)UL0U#f?kBaL5wu`U;U?c

Jmj1MT@9 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 + + + + + Compression App + + + +

+

Welcome to Free Compression Server

+ +
+ + + +
+
+ + diff --git a/public/style.css b/public/style.css new file mode 100644 index 0000000..4e69b8c --- /dev/null +++ b/public/style.css @@ -0,0 +1,24 @@ +body { + padding: 0; + margin: 0; +} + +.main-header { + font-size: 36px; + line-height: 140%; + margin-bottom: 40px; +} + +.main-container { + height: 100vh; + width: 100vw; + padding: 2em; + + color: white; + box-sizing: border-box; + background-color: tan; + + font-family: Arial, Helvetica, sans-serif; + font-size: 16px; + line-height: 100%; +} diff --git a/public/uploads/c8l5fhrccb1uk7kppkn7trrkm b/public/uploads/c8l5fhrccb1uk7kppkn7trrkm new file mode 100644 index 0000000..02866de --- /dev/null +++ b/public/uploads/c8l5fhrccb1uk7kppkn7trrkm @@ -0,0 +1,3 @@ +Surgo doloribus officiis texo sto eius annus odit occaecati. Testimonium vicinus absconditus stillicidium bis capillus vulpes. Cotidie ulterius claustrum temptatio cometes sumptus earum sumptus. +Sopor cupiditate tamquam careo armarium. Pecus peior absque depereo statim credo vilitas vulgivagus. Quis thema verbera videlicet vulariter stips veritas. +Sunt umerus creber. Vomer talus usque aeternus vomica pauci timor sub aqua. Depulso adhuc tolero. \ No newline at end of file diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..caaa44c 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,257 @@ 'use strict'; +const http = require('http'); +const path = require('path'); +const fs = require('fs'); +const zlib = require('zlib'); +const { formidable } = require('formidable'); +const { pipeline } = require('stream'); +const mime = require('mime-types'); + function createServer() { - /* Write your code here */ - // Return instance of http.Server class + const server = http.createServer(); + const PUBLIC_PATH = path.join(__dirname, '..', 'public'); + + server.on('request', (req, res) => { + // index.html and style.css responses + if (req.url === '/') { + res.setHeader('Content-type', 'text/html'); + + const INDEX_PATH = path.resolve(PUBLIC_PATH, 'index.html'); + + if (!fs.existsSync(INDEX_PATH)) { + res.statusCode = 404; + res.end('Not Found Page'); + } + + const file = fs.createReadStream(INDEX_PATH); + + file.pipe(res); + + res.on('close', () => { + file.destroy(); + }); + + return; + } + + if (req.url === '/favicon.ico') { + const favicon = fs.createReadStream(path.resolve('public/favicon.ico')); + + res.statusCode = 200; + favicon.pipe(res); + + return; + } + + if (req.url === '/style.css') { + res.setHeader('Content-type', 'text/css'); + + const STYLES_PATH = path.resolve(PUBLIC_PATH, 'style.css'); + + if (!fs.existsSync(STYLES_PATH)) { + res.end(''); + } + + const cssFile = fs.createReadStream(STYLES_PATH); + + cssFile.pipe(res); + + res.on('close', () => { + cssFile.destroy(); + }); + + return; + } + + // Compression Logic + if (req.url === '/compress') { + // check, if method === POST, otherwise 400 + if (req.method !== 'POST') { + res.statusCode = 400; + res.end('Wrong request method!'); + + return; + } + + // let's create a Form object from Formidable lib, + // which will help us to deal with Form data + + const form = formidable({ + multiples: false, + uploadDir: path.resolve('public/uploads'), + }); + + // use mime-types from NPM to detect content-type of archive + let mimeType = ''; + + form.parse(req, (err, fields, files) => { + if (err) { + res.statusCode = 400; + res.end(err); + } + // let's fetch compressionType & file obiviosly + + const compressionType = fields.compressionType[0]; + + const uploadedFile = files.filePicker[0]; + + // double sanity for non-valid input + if (!compressionType || !uploadedFile) { + res.statusCode = 400; + res.end('Invalid input!'); + } + + // let's rename uploaded file to it's original filename + + const correctUploadedFilePath = path.resolve( + 'public/uploads', + uploadedFile.originalFilename, + ); + + fs.rename(uploadedFile.filepath, correctUploadedFilePath, (_err) => { + if (_err) { + res.statusCode = 500; + res.end('Error while renaming uploaded file!'); + } + }); + + const readStream = fs.createReadStream(correctUploadedFilePath); + + // if everything is OK, let's pipe uploaded file to chosen compression + switch (compressionType) { + case 'gzip': { + const gzip = zlib.createGzip(); + + // let's get rid from whitespaces (if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + + const zippedFilePath = + path + .basename(correctUploadedFilePath) + .replaceAll(' ', '-') + .replaceAll(',', '-') + .replaceAll('.', '-') + '.gzip'; + + mimeType = mime.contentType('gzip'); + + pipeline(readStream, gzip, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); + + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; + + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'gzip'); + + res.on('close', () => { + readStream.destroy(); + }); + + return; + } + + case 'deflate': { + const deflate = zlib.createDeflate(); + + // let's get rid from whitespaces (if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + + const zippedFilePath = + path + .basename(correctUploadedFilePath) + .replaceAll(' ', '-') + .replaceAll(',', '-') + .replaceAll('.', '-') + '.dfl'; + + mimeType = mime.contentType('deflate'); + + pipeline(readStream, deflate, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); + + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; + + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'deflate'); + + res.on('close', () => { + readStream.destroy(); + }); + + return; + } + + case 'br': { + const deflate = zlib.createBrotliCompress(); + + // let's get rid from whitespaces (if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + + const zippedFilePath = + path + .basename(correctUploadedFilePath) + .replaceAll(' ', '-') + .replaceAll(',', '-') + .replaceAll('.', '-') + '.br'; + + mimeType = mime.contentType('br'); + + pipeline(readStream, deflate, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); + + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; + + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'br'); + + res.on('close', () => { + readStream.destroy(); + }); + + return; + } + + default: { + res.statusCode = 400; + res.end('Wrong type of compression sended!'); + } + } + }); + + return; + } + + res.statusCode = 404; + res.end('Not found'); + }); + + return server; } module.exports = { From cdc497569d16850bb40dc35006b60ba4b12da794 Mon Sep 17 00:00:00 2001 From: Anton Kuchmasov Date: Sun, 26 Oct 2025 14:20:34 +0200 Subject: [PATCH 2/3] solution --- public/uploads/c8l5fhrccb1uk7kppkn7trrkm | 3 - public/uploads/gtteqy7ii22sje3iibskw93rm | 3 + src/createServer.js | 194 +++++++++++------------ 3 files changed, 99 insertions(+), 101 deletions(-) delete mode 100644 public/uploads/c8l5fhrccb1uk7kppkn7trrkm create mode 100644 public/uploads/gtteqy7ii22sje3iibskw93rm diff --git a/public/uploads/c8l5fhrccb1uk7kppkn7trrkm b/public/uploads/c8l5fhrccb1uk7kppkn7trrkm deleted file mode 100644 index 02866de..0000000 --- a/public/uploads/c8l5fhrccb1uk7kppkn7trrkm +++ /dev/null @@ -1,3 +0,0 @@ -Surgo doloribus officiis texo sto eius annus odit occaecati. Testimonium vicinus absconditus stillicidium bis capillus vulpes. Cotidie ulterius claustrum temptatio cometes sumptus earum sumptus. -Sopor cupiditate tamquam careo armarium. Pecus peior absque depereo statim credo vilitas vulgivagus. Quis thema verbera videlicet vulariter stips veritas. -Sunt umerus creber. Vomer talus usque aeternus vomica pauci timor sub aqua. Depulso adhuc tolero. \ No newline at end of file diff --git a/public/uploads/gtteqy7ii22sje3iibskw93rm b/public/uploads/gtteqy7ii22sje3iibskw93rm new file mode 100644 index 0000000..ffeaecc --- /dev/null +++ b/public/uploads/gtteqy7ii22sje3iibskw93rm @@ -0,0 +1,3 @@ +Apud vehemens atque timidus certe dolor vehemens facere cura sollicito. Curis timor absens cavus defero cumque arcesso. Deprecator facilis accendo peccatus sopor crapula cogo voluptate depromo. +Subiungo vilicus adsuesco adinventitias corrupti aurum curiositas ipsa cibus. Deprecator absque nam voro crastinus tametsi labore veritatis vorax. Deludo doloribus admoneo ubi turpis adinventitias blanditiis thorax verbum. +Iste textilis victoria chirographum. Bellicus acies claudeo decens alo. Eum curia teneo degero sto triduana amet suffragium arto. \ No newline at end of file diff --git a/src/createServer.js b/src/createServer.js index caaa44c..7210b66 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -22,6 +22,8 @@ function createServer() { if (!fs.existsSync(INDEX_PATH)) { res.statusCode = 404; res.end('Not Found Page'); + + return; } const file = fs.createReadStream(INDEX_PATH); @@ -51,6 +53,8 @@ function createServer() { if (!fs.existsSync(STYLES_PATH)) { res.end(''); + + return; } const cssFile = fs.createReadStream(STYLES_PATH); @@ -89,6 +93,8 @@ function createServer() { if (err) { res.statusCode = 400; res.end(err); + + return; } // let's fetch compressionType & file obiviosly @@ -100,6 +106,8 @@ function createServer() { if (!compressionType || !uploadedFile) { res.statusCode = 400; res.end('Invalid input!'); + + return; } // let's rename uploaded file to it's original filename @@ -113,135 +121,125 @@ function createServer() { if (_err) { res.statusCode = 500; res.end('Error while renaming uploaded file!'); + + return; } - }); - const readStream = fs.createReadStream(correctUploadedFilePath); + const readStream = fs.createReadStream(correctUploadedFilePath); - // if everything is OK, let's pipe uploaded file to chosen compression - switch (compressionType) { - case 'gzip': { - const gzip = zlib.createGzip(); + // if everything is OK, let's pipe uploaded file to chosen compression + switch (compressionType) { + case 'gzip': { + const gzip = zlib.createGzip(); - // let's get rid from whitespaces (if present) in original filename - // because they can provoke an error: - // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + // let's get rid from whitespaces(if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION - const zippedFilePath = - path - .basename(correctUploadedFilePath) - .replaceAll(' ', '-') - .replaceAll(',', '-') - .replaceAll('.', '-') + '.gzip'; + const zippedFilePath = + path.basename(correctUploadedFilePath) + '.gzip'; - mimeType = mime.contentType('gzip'); + mimeType = mime.contentType('gzip'); - pipeline(readStream, gzip, res, (error) => { - res.statusCode = 500; - res.end(`Error while piping: ${error}`); - }); + pipeline(readStream, gzip, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); - // and if everything is OK, let's finalize - // our Response with all needed headers - res.statusCode = 200; + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; - res.setHeader( - 'Content-Disposition', - `attachment; filename=${zippedFilePath}`, - ); - res.setHeader('Content-Type', mimeType); - res.setHeader('Content-Encoding', 'gzip'); + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'gzip'); - res.on('close', () => { - readStream.destroy(); - }); + res.on('close', () => { + readStream.destroy(); + }); - return; - } + return; + } - case 'deflate': { - const deflate = zlib.createDeflate(); + case 'deflate': { + const deflate = zlib.createDeflate(); - // let's get rid from whitespaces (if present) in original filename - // because they can provoke an error: - // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + // let's get rid from whitespaces(if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION - const zippedFilePath = - path - .basename(correctUploadedFilePath) - .replaceAll(' ', '-') - .replaceAll(',', '-') - .replaceAll('.', '-') + '.dfl'; + const zippedFilePath = + path.basename(correctUploadedFilePath) + '.dfl'; - mimeType = mime.contentType('deflate'); + mimeType = mime.contentType('deflate'); - pipeline(readStream, deflate, res, (error) => { - res.statusCode = 500; - res.end(`Error while piping: ${error}`); - }); + pipeline(readStream, deflate, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); - // and if everything is OK, let's finalize - // our Response with all needed headers - res.statusCode = 200; + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; - res.setHeader( - 'Content-Disposition', - `attachment; filename=${zippedFilePath}`, - ); - res.setHeader('Content-Type', mimeType); - res.setHeader('Content-Encoding', 'deflate'); + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'deflate'); - res.on('close', () => { - readStream.destroy(); - }); + res.on('close', () => { + readStream.destroy(); + }); - return; - } + return; + } - case 'br': { - const deflate = zlib.createBrotliCompress(); + case 'br': { + const deflate = zlib.createBrotliCompress(); - // let's get rid from whitespaces (if present) in original filename - // because they can provoke an error: - // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION + // let's get rid from whitespaces(if present) in original filename + // because they can provoke an error: + // ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION - const zippedFilePath = - path - .basename(correctUploadedFilePath) - .replaceAll(' ', '-') - .replaceAll(',', '-') - .replaceAll('.', '-') + '.br'; + const zippedFilePath = + path.basename(correctUploadedFilePath) + '.br'; - mimeType = mime.contentType('br'); + mimeType = mime.contentType('br'); - pipeline(readStream, deflate, res, (error) => { - res.statusCode = 500; - res.end(`Error while piping: ${error}`); - }); + pipeline(readStream, deflate, res, (error) => { + res.statusCode = 500; + res.end(`Error while piping: ${error}`); + }); - // and if everything is OK, let's finalize - // our Response with all needed headers - res.statusCode = 200; + // and if everything is OK, let's finalize + // our Response with all needed headers + res.statusCode = 200; - res.setHeader( - 'Content-Disposition', - `attachment; filename=${zippedFilePath}`, - ); - res.setHeader('Content-Type', mimeType); - res.setHeader('Content-Encoding', 'br'); + res.setHeader( + 'Content-Disposition', + `attachment; filename=${zippedFilePath}`, + ); + res.setHeader('Content-Type', mimeType); + res.setHeader('Content-Encoding', 'br'); - res.on('close', () => { - readStream.destroy(); - }); + res.on('close', () => { + readStream.destroy(); + }); - return; - } + return; + } - default: { - res.statusCode = 400; - res.end('Wrong type of compression sended!'); + default: { + res.statusCode = 400; + res.end('Wrong type of compression sended!'); + } } - } + }); }); return; From f1f8f5501539ed3cba311ceb17eb524d1829825e Mon Sep 17 00:00:00 2001 From: Anton Kuchmasov Date: Sun, 26 Oct 2025 14:30:18 +0200 Subject: [PATCH 3/3] solution --- public/index.html | 2 +- public/uploads/gtteqy7ii22sje3iibskw93rm | 3 -- public/uploads/prize.bin | 3 ++ src/createServer.js | 45 ++++++++++++------------ 4 files changed, 27 insertions(+), 26 deletions(-) delete mode 100644 public/uploads/gtteqy7ii22sje3iibskw93rm create mode 100644 public/uploads/prize.bin diff --git a/public/index.html b/public/index.html index 9939638..24bb9fc 100644 --- a/public/index.html +++ b/public/index.html @@ -16,7 +16,7 @@

Welcome to Free Compression Server

enctype="multipart/form-data" action="http://localhost:5700/compress" > - +