Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ To pass the task you also need to implement a server that:
- respond with 400 status code if trying to compress a file with an unsupported compression type
- respond with 200 status code and compressed file if the form is valid


**Read [the guideline](https://github.com/mate-academy/js_task-guideline/blob/master/README.md) before start**
124 changes: 122 additions & 2 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,128 @@
'use strict';

const http = require('http');
const fs = require('fs');
const path = require('path');
const { pipeline, Readable } = require('stream');
const zlib = require('zlib');

const COMPRESSION_TYPES = ['gzip', 'deflate', 'br'];

function sendStatus(res, code) {
if (!res.headersSent) {
res.writeHead(code);
res.end();
}
}

function createServer() {
/* Write your code here */
// Return instance of http.Server class
return http.createServer((req, res) => {
const { url, method, headers } = req;

if (url === '/' && method === 'GET') {
const htmlPath = path.join(__dirname, 'index.html');
const readStream = fs.createReadStream(htmlPath, 'utf-8');

res.writeHead(200, { 'Content-Type': 'text/html' });

pipeline(readStream, res, (err) => {
if (err) {
sendStatus(res, 500);
}
});

return;
}

if (method === 'GET' && url === '/compress') {
sendStatus(res, 400);

return;
}

if (method === 'POST' && url === '/compress') {
const contentType = headers['content-type'];

if (!contentType || !contentType.includes('multipart/form-data')) {
return sendStatus(res, 400);
}

const boundary = '--' + contentType.split('boundary=')[1];

let body = Buffer.alloc(0);

req.on('data', (chunk) => {
body = Buffer.concat([body, chunk]);
});

req.on('end', () => {
const parts = body.toString('binary').split(boundary);

let fileBuffer;
let fileName;
let compressionType;

for (const part of parts) {
if (part.includes('name="compressionType"')) {
compressionType = part.split('\r\n\r\n')[1]?.trim();
}

if (part.includes('name="file"')) {
const match = part.match(/filename="(.+?)"/);

if (match) {
fileName = match[1];

const fileContent = part.split('\r\n\r\n')[1];

fileBuffer = Buffer.from(
fileContent.slice(0, fileContent.lastIndexOf('\r\n')),
'binary',
);
}
}
}

if (!fileBuffer || !fileName || !compressionType) {
return sendStatus(res, 400);
}

if (!COMPRESSION_TYPES.includes(compressionType)) {
return sendStatus(res, 400);
}

let compressor;
let extension;

if (compressionType === 'gzip') {
compressor = zlib.createGzip();
extension = '.gzip';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requirement specifies the file extension for gzip compression should be .gz.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests gave error with ".gz"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirement specifies the extension for gzip should be .gz, not .gzip.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file extension for gzip compression is still incorrect. According to the task description, it should be .gz.

}

if (compressionType === 'deflate') {
compressor = zlib.createDeflate();
extension = '.deflate';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the task description, the file extension for deflate compression should be .dfl.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests gave error with ".dfl"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the task description, the extension for deflate should be .dfl.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file extension for deflate compression also needs to be corrected. The requirement specifies using .dfl for this compression type.

}

if (compressionType === 'br') {
compressor = zlib.createBrotliCompress();
extension = '.br';
}

res.writeHead(200, {
'Content-Disposition': `attachment; filename=${fileName}${extension}`,
'Content-Type': 'application/octet-stream',
});

pipeline(Readable.from(fileBuffer), compressor, res, () => {});
});

return;
}

res.statusCode = 404;
res.end('Not Found');
});
}

module.exports = {
Expand Down
24 changes: 24 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/compress" method="POST" enctype="multipart/form-data">
<label for="file">Select file: </label>
<input type="file" id="file" name="file" required>

<label for="compressionType">Comresion</label>
<select name="compressionType" id="compressionType" required>
<option value="gzip">Gzip</option>
<option value="deflate">Deflate</option>
<option value="br">Brotli</option>
</select>
<br />

<button type="submit">Compress</button>
</form>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This closing </form> tag does not have a matching opening <form> tag. This is because a <br> tag was used on line 9 instead.

</body>
</html>