Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File.resume() docx file busboy finish event #191

Closed
AdSegura opened this issue Jan 22, 2019 · 5 comments
Closed

File.resume() docx file busboy finish event #191

AdSegura opened this issue Jan 22, 2019 · 5 comments

Comments

@AdSegura
Copy link

Hello!

In order to discard not allowed extensions I use file.resume() to get off not allowed files and let start with another or finish the form upload.

All went smooth until some type of .docx knock my door.

The problem is when we upload this type of file, Busboy never gets finish signal and browser connection hangs until time out, with other not allowed files they get rejected, Busboy knows when the upload finished.

$> node -v
    v10.15.0

Busboy version:

    "busboy": "^0.3.0",

To reproduce this behaviour:

$> curl https://transfer.sh/K1mPC/f2.docx -o f2.docx
$> file -bi 'f2.docx' 
    application/zip; charset=binary
$> node server.bug.js 
$> curl -i -X POST -H "Content-Type: multipart/form-data" -F "data=@f2.docx" http://localhost:3000/busboy

    HTTP/1.1 100 Continue

    curl: (56) Recv failure: Connection reinitialized by the remote machine

-Server Output:

    readFirstBytes...
    File [data]: filename: f2.docx, encoding: 7bit, mimetype: application/octet-stream
    readFirstBytes...
    Rejected file of type docx
    File ON ERROR [f2.docx] ERROR ->  EXT NOT ALLOWED
    File ON END [f2.docx] Finished
    Reached the end, but did not read anything.

I can upload other doc or docx files and they get rejected and busboy.on('finish') gets executed:

$> curl https://transfer.sh/pyFD3/f1.docx -o f1.docx
$> file -bi 'f1.docx' 
    application/octet-stream; charset=binary
$> curl -i -X POST -H "Content-Type: multipart/form-data" -F "data=@f1.docx" http://localhost:3000/busboy

    HTTP/1.1 100 Continue

    HTTP/1.1 200 OK
    Date: Tue, 22 Jan 2019 07:35:28 GMT
    Connection: keep-alive
    Content-Length: 18

    Done parsing form!

-Server Output:

    readFirstBytes...
    File [data]: filename: f1.docx, encoding: 7bit, mimetype: application/octet-stream
    readFirstBytes...
    Rejected file of type docx
    File ON ERROR [f1.docx] ERROR ->  EXT NOT ALLOWED
    File ON END [f1.docx] Finished
    Reached the end, but did not read anything.
    Done parsing form!

server.js

const http = require('http');
const fs = require('fs');
const path = require('path');
const Busboy = require('busboy');
const fileType = require('file-type');
const port = 3000;
const host = "localhost";

/**
 * Create Server
 */
const server = http.createServer((req, res) => {
   const url = req.url;
   if (url === '/') {
      return renderForm(res);
   } else if (url === '/busboy' && req.method === 'POST') {
      return uploadFiles(req, res);
   } else {
      return notFound(res);
   }
})

server.listen(port, host, () => {
   console.log(`Server running at http://${host}:${port}/`);
 });


/**
 * Upload Files
 * 
 * @param {*} req 
 * @param {*} res 
 */
const uploadFiles = (req, res) => {
   const opt = {
      dest: path.join(__dirname, '.'),
      limits: {
         fileSize: 1024 * 1024,
         files: 2
      }
   };

   var busboy = new Busboy({
      headers: req.headers,
      opt: opt
   });


   busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {

      function readFirstBytes() {
         console.log("readFirstBytes...")
         var chunk = file.read(fileType.minimumBytes);
         if (!chunk) return file.once('readable', readFirstBytes);
         const type = fileType(chunk);
         let ext = "foo";
         if (type) ext = type.ext
         file.unshift(chunk);

         if (ext === 'jpeg' || ext === 'jpg' || ext === 'png') {
            console.log("checkExtention allow ---> ", ext)
            file.pipe(fs.createWriteStream('./' + filename));
         } else {
            console.log('Rejected file of type ' + ext);
            file.resume().on('end', () => {
               console.log('Reached the end, but did not read anything.');
            }); // Drain file stream to continue processing form
            file.emit('error', 'EXT NOT ALLOWED')
            file.emit('close')
            file.emit('end')
         }
      }

      readFirstBytes();

      console.log('File [' + fieldname + ']: filename: ' + filename + ', encoding: ' + encoding + ', mimetype: ' + mimetype);

      file.on('error', function (e) {
         console.log('File ON ERROR [' + filename + '] ERROR -> ', e);
      }).on('end', function () {
         console.log('File ON END [' + filename + '] Finished');
      }).on('limit', () => {
         console.log(`bytes limit ${opt.limits.fileSize} exceeded on File: ${filename}`);
         file.resume(); //fires finish
      });

   }).on('filesLimit', () => {
      console.log('MAX FILES REACHED, LIMIT IS: ', opt.limits.files)
   }).on('finish', (f) => {
      console.log('Done parsing form!');
      return res.end('Done parsing form!');
   });

   req.pipe(busboy);

}

/**
 * Not Found Res
 * 
 * @param {*} res 
 */
const notFound = (res) => {
   res.statusCode = 404;
   res.setHeader('Content-Type', 'text/html');
   res.end('<h1>404 NOT FOUND</h1>');
}

/**
 * Render Upload Form
 * 
 * @param {*} res 
 */
const renderForm = (res) => {
   res.write('<h1>Upload Files<h1>'); 
   res.end(
      '<form action="/busboy" enctype="multipart/form-data" method="post">' +
      '<input type="text" name="title"><br>' +
      '<input type="file" name="upload" multiple="multiple"><br>' +
      '<input type="submit" value="Upload">' +
      '</form>'
   );
}

Thanks for reading.

@AdSegura
Copy link
Author

Hi.

Think I just solve it, still doing some tests.

Calling file.resume() on next tick do the trick.

function readFirstBytes() {
         console.log("readFirstBytes...")
         var chunk = file.read(fileType.minimumBytes);
         if (!chunk) return file.once('readable', readFirstBytes);
         const type = fileType(chunk);
         let ext = "foo";
         if (type) ext = type.ext
         file.unshift(chunk);

         if (ext === 'jpeg' || ext === 'jpg' || ext === 'png') {
            console.log("checkExtention allow ---> ", ext)
            file.pipe(fs.createWriteStream('./' + filename));
         } else {
            console.log('Rejected file of type ' + ext);
            process.nextTick(function() {
                file.resume().on('end', () => {
                    console.log('Reached the end, but did not read anything.');
                });                                    
            });
      }

@mscdex
Copy link
Owner

mscdex commented Jan 22, 2019

I think this is a bug in node v10.x. node v8.x and node master currently work as you're expecting.

@mscdex
Copy link
Owner

mscdex commented Jan 22, 2019

Upstream bug report: nodejs/node#25642

@AdSegura
Copy link
Author

Thanks for your responses @mscdex appreciate it.

@mscdex
Copy link
Owner

mscdex commented Feb 9, 2019

Alright as noted in the upstream issue, this should've been fixed in node v10.15.1.

@mscdex mscdex closed this as completed Feb 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants