Skip to content
Open
Changes from 1 commit
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
105 changes: 103 additions & 2 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,109 @@
'use strict';

/* eslint-disable no-console */

const { Server } = require('node:http');
const fs = require('node:fs');
const zlib = require('node:zlib');

const { formidable } = require('formidable');
const { pipeline } = require('node:stream');

function createServer() {
/* Write your code here */
// Return instance of http.Server class
const server = new Server();

server.on('request', async (req, res) => {
const path = req.url;
const method = req.method;

if (method === 'GET' && path === '/') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');

const file = fs.createReadStream('public/index.html');

file.on('error', () => {
res.statusCode = 404;
res.end('No such file');
});

file.on('close', () => {
console.log('connection close');
file.destroy();
});
file.pipe(res);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When serving the index HTML you create a read stream and pipe it to the response, but you never set an explicit error handler on the response stream for pipeline errors. Consider using pipeline for consistency (reads + piping) to ensure stream errors close the response properly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The server responds with 400 for GET /compress as required, and 404 for other endpoints — good. However, ensure public/index.html exists in the expected path; missing file produces 404 which is handled, but tests expect the root GET to return the HTML. If tests require synchronous file existence you may need to ensure that file is present in the project.

} else if (method === 'POST' && path === '/compress') {
const form = formidable({});

form.parse(req, (err, fields, files) => {
if (err) {
res.statusCode = 400;
res.end();

return;
}

if (!files.file?.[0] || !fields.compressionType?.[0]) {
res.statusCode = 400;
res.end();

return;
}

const uploadedFilePath = files.file[0].filepath;
const readStream = fs.createReadStream(uploadedFilePath);
const compressionType = fields.compressionType[0];
const fileName = files.file[0].originalFilename;
Comment on lines +51 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Form parsing uses formidable and expects arrays (files.file[0], fields.compressionType[0]). This is fine only if formidable is configured to return arrays; default behavior may provide single values. Verify that the tests/invocation provide fields as arrays; otherwise access files.file and fields.compressionType directly. If you intend to rely on arrays, document or configure formidable accordingly.

Comment on lines +51 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using array indexing for files.file and fields.compressionType assumes formidable returns arrays. This is brittle if formidable configuration changes — access values in a shape-agnostic way (e.g., handle both arrays and single values) or configure/document formidable to always return arrays. This relates to requirements to validate the form and could cause false 400 responses. (Around lines 51, 58, 60, 61)


let fileCompression;
let extension = '';

switch (compressionType) {
case 'gzip':
fileCompression = zlib.createGzip();
extension = '.gz';

break;
case 'deflate':
fileCompression = zlib.createDeflate();
extension = '.dfl';

break;
case 'br':
fileCompression = zlib.createBrotliCompress();
extension = '.br';

break;
default:
res.statusCode = 400;
res.end();

return;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'application/octet-stream');

res.setHeader(
'Content-Disposition',
`attachment; filename=${fileName}${extension}`,
Comment on lines +91 to +93
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The Content-Disposition header uses an unquoted filename. Filenames containing spaces or special characters may break downloads. Wrap the filename in quotes like attachment; filename="${fileName}${extension}". (lines 91-93)

);
Comment on lines +91 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You set Content-Disposition header to attachment; filename=${fileName}${extension} without escaping or wrapping filename. If fileName contains spaces or special chars the header can become invalid. Wrap the filename in double quotes: attachment; filename="${fileName}${extension}".


pipeline(readStream, fileCompression, res, (error) => {
if (error) {
res.end();
}
});
});
} else if (method === 'GET' && path === '/compress') {
res.statusCode = 400;
res.end();
} else {
res.statusCode = 404;
res.end();
}
});

return server;
}

module.exports = {
Expand Down
Loading