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..8fba7a5 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,115 @@ +/* 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: { + extension: 'gz', + createStream: () => zlib.createGzip(), + }, + deflate: { + extension: 'dfl', + createStream: () => zlib.createDeflate(), + }, + br: { + extension: 'br', + createStream: () => zlib.createBrotliCompress(), + }, + }; + + if (url.pathname === '/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 compressionConfig = compressionMap[compressionType]; + + if (!compressionConfig) { + res.statusCode = 400; + res.end('Unsupported compression type'); + + return; + } + + const readStream = fs.createReadStream(file.filepath); + const compressStream = compressionConfig.createStream(); + const outputFileName = `${file.originalFilename}.${compressionConfig.extension}`; + + 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 (url.pathname === '/compress' && req.method === 'GET') { + res.statusCode = 400; + res.end('Wrong URL'); + + return; + } + + 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); + + return; + } + + res.statusCode = 404; + res.end('Not Found'); + }); } module.exports = { 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,