Skip to content

Commit

Permalink
Merge pull request #714 from VeliovGroup/dev
Browse files Browse the repository at this point in the history
v1.12.2
- __HTTP upload fixes__:
  - πŸ‘¨β€πŸ’» Fix conditional exception when HTTP upload left unhandled open due to parsed `body`
  - πŸ‘¨β€πŸ”¬ Add `20000ms` timeout for "zombie" HTTP upload requests
- πŸ“‹ Upgrade documentation with using `.includes()` instead of `.indexOf()` for strings
- πŸ‘¨β€πŸ’» Upgrade codebase with using `.includes()` instead of `.indexOf()` for strings
  • Loading branch information
dr-dimitru authored Oct 7, 2019
2 parents a7d5636 + 0688a8e commit b240418
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 141 deletions.
4 changes: 2 additions & 2 deletions .versions
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ geojson-utils@1.0.10
http@1.4.2
id-map@1.1.0
inter-process-messaging@0.1.0
local-test:ostrio:files@1.12.1
local-test:ostrio:files@1.12.2
logging@1.1.20
meteor@1.9.3
minimongo@1.4.5
Expand All @@ -36,7 +36,7 @@ mongo-id@1.0.7
npm-mongo@3.1.2
ordered-dict@1.1.0
ostrio:cookies@2.4.1
ostrio:files@1.12.1
ostrio:files@1.12.2
promise@0.11.2
random@1.1.0
reactive-var@1.0.11
Expand Down
2 changes: 1 addition & 1 deletion core.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export default class FilesCollectionCore extends EventEmitter {
* @returns {Object}
*/
_getExt(fileName) {
if (!!~fileName.indexOf('.')) {
if (fileName.includes('.')) {
const extension = (fileName.split('.').pop().split('?')[0] || '').toLowerCase();
return { ext: extension, extension, extensionWithDot: `.${extension}` };
}
Expand Down
2 changes: 1 addition & 1 deletion docs/constructor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ const Images = new FilesCollection({
const { Magic, MAGIC_MIME_TYPE } = require('mmmagic');
const magic = new Magic(MAGIC_MIME_TYPE);
magic.detectFile(file.path, Meteor.bindEnvironment((err, mimeType) => {
if (err || !~mimeType.indexOf('image')) {
if (err || !mimeType.includes('image')) {
// is not a real image --> delete
console.log('onAfterUpload, not an image: ', file.path);
console.log('deleted', file.path);
Expand Down
4 changes: 2 additions & 2 deletions lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ for (let i = 0; i < _helpers.length; i++) {
*/
const fixJSONParse = function(obj) {
for (let key in obj) {
if (helpers.isString(obj[key]) && !!~obj[key].indexOf('=--JSON-DATE--=')) {
if (helpers.isString(obj[key]) && obj[key].includes('=--JSON-DATE--=')) {
obj[key] = obj[key].replace('=--JSON-DATE--=', '');
obj[key] = new Date(parseInt(obj[key]));
} else if (helpers.isObject(obj[key])) {
Expand All @@ -134,7 +134,7 @@ const fixJSONParse = function(obj) {
v = obj[key][i];
if (helpers.isObject(v)) {
obj[key][i] = fixJSONParse(v);
} else if (helpers.isString(v) && !!~v.indexOf('=--JSON-DATE--=')) {
} else if (helpers.isString(v) && v.includes('=--JSON-DATE--=')) {
v = v.replace('=--JSON-DATE--=', '');
obj[key][i] = new Date(parseInt(v));
}
Expand Down
2 changes: 1 addition & 1 deletion package.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package.describe({
name: 'ostrio:files',
version: '1.12.1',
version: '1.12.2',
summary: 'Upload files to Meteor application, with 3rd party storage support: AWS:S3, GridFS and other',
git: 'https://github.com/VeliovGroup/Meteor-Files',
documentation: 'README.md'
Expand Down
275 changes: 143 additions & 132 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,169 +465,180 @@ export class FilesCollection extends FilesCollectionCore {
}
}

if (!this.disableUpload && !!~httpReq._parsedUrl.path.indexOf(`${this.downloadRoute}/${this.collectionName}/__upload`)) {
if (httpReq.method === 'POST') {
const handleError = (_error) => {
let error = _error;
console.warn('[FilesCollection] [Upload] [HTTP] Exception:', error);
console.trace();

if (!httpResp.headersSent) {
httpResp.writeHead(500);
if (!this.disableUpload && httpReq._parsedUrl.path.includes(`${this.downloadRoute}/${this.collectionName}/__upload`)) {
if (httpReq.method !== 'POST') {
next();
return;
}

const handleError = (_error) => {
let error = _error;
console.warn('[FilesCollection] [Upload] [HTTP] Exception:', error);
console.trace();

if (!httpResp.headersSent) {
httpResp.writeHead(500);
}

if (!httpResp.finished) {
if (helpers.isObject(error) && helpers.isFunction(error.toString)) {
error = error.toString();
}

if (!httpResp.finished) {
if (helpers.isObject(error) && helpers.isFunction(error.toString)) {
error = error.toString();
}
if (!helpers.isString(error)) {
error = 'Unexpected error!';
}

if (!helpers.isString(error)) {
error = 'Unexpected error!';
}
httpResp.end(JSON.stringify({ error }));
}
};

let body = '';
const handleData = () => {
try {
let opts;
let result;
let user;

httpResp.end(JSON.stringify({ error }));
if (httpReq.headers['x-mtok'] && this._getUserId(httpReq.headers['x-mtok'])) {
user = {
userId: this._getUserId(httpReq.headers['x-mtok'])
};
} else {
user = this._getUser({request: httpReq, response: httpResp});
}
};

let body = '';
httpReq.on('data', (data) => bound(() => {
body += data;
}));
if (httpReq.headers['x-start'] !== '1') {
opts = {
fileId: httpReq.headers['x-fileid']
};

httpReq.on('end', () => bound(() => {
try {
let opts;
let result;
let user;

if (httpReq.headers['x-mtok'] && this._getUserId(httpReq.headers['x-mtok'])) {
user = {
userId: this._getUserId(httpReq.headers['x-mtok'])
};
if (httpReq.headers['x-eof'] === '1') {
opts.eof = true;
} else {
user = this._getUser({request: httpReq, response: httpResp});
opts.binData = Buffer.from(body, 'base64');
opts.chunkId = parseInt(httpReq.headers['x-chunkid']);
}

if (httpReq.headers['x-start'] !== '1') {
opts = {
fileId: httpReq.headers['x-fileid']
};
const _continueUpload = this._continueUpload(opts.fileId);
if (!_continueUpload) {
throw new Meteor.Error(408, 'Can\'t continue upload, session expired. Start upload again.');
}

if (httpReq.headers['x-eof'] === '1') {
opts.eof = true;
} else {
opts.binData = Buffer.from(body, 'base64');
opts.chunkId = parseInt(httpReq.headers['x-chunkid']);
}
({result, opts} = this._prepareUpload(Object.assign(opts, _continueUpload), user.userId, 'HTTP'));

const _continueUpload = this._continueUpload(opts.fileId);
if (!_continueUpload) {
throw new Meteor.Error(408, 'Can\'t continue upload, session expired. Start upload again.');
}

({result, opts} = this._prepareUpload(Object.assign(opts, _continueUpload), user.userId, 'HTTP'));
if (opts.eof) {
this._handleUpload(result, opts, (_error) => {
let error = _error;
if (error) {
if (!httpResp.headersSent) {
httpResp.writeHead(500);
}

if (opts.eof) {
this._handleUpload(result, opts, (_error) => {
let error = _error;
if (error) {
if (!httpResp.headersSent) {
httpResp.writeHead(500);
if (!httpResp.finished) {
if (helpers.isObject(error) && helpers.isFunction(error.toString)) {
error = error.toString();
}

if (!httpResp.finished) {
if (helpers.isObject(error) && helpers.isFunction(error.toString)) {
error = error.toString();
}

if (!helpers.isString(error)) {
error = 'Unexpected error!';
}

httpResp.end(JSON.stringify({ error }));
if (!helpers.isString(error)) {
error = 'Unexpected error!';
}
}

if (!httpResp.headersSent) {
httpResp.writeHead(200);
httpResp.end(JSON.stringify({ error }));
}
}

if (helpers.isObject(result.file) && result.file.meta) {
result.file.meta = fixJSONStringify(result.file.meta);
}
if (!httpResp.headersSent) {
httpResp.writeHead(200);
}

if (!httpResp.finished) {
httpResp.end(JSON.stringify(result));
}
});
return;
}
if (helpers.isObject(result.file) && result.file.meta) {
result.file.meta = fixJSONStringify(result.file.meta);
}

this.emit('_handleUpload', result, opts, NOOP);
if (!httpResp.finished) {
httpResp.end(JSON.stringify(result));
}
});
return;
}

if (!httpResp.headersSent) {
httpResp.writeHead(204);
}
if (!httpResp.finished) {
httpResp.end();
}
} else {
try {
opts = JSON.parse(body);
} catch (jsonErr) {
console.error('Can\'t parse incoming JSON from Client on [.insert() | upload], something went wrong!', jsonErr);
opts = {file: {}};
}
this.emit('_handleUpload', result, opts, NOOP);

if (!helpers.isObject(opts.file)) {
opts.file = {};
}
if (!httpResp.headersSent) {
httpResp.writeHead(204);
}
if (!httpResp.finished) {
httpResp.end();
}
} else {
try {
opts = JSON.parse(body);
} catch (jsonErr) {
console.error('Can\'t parse incoming JSON from Client on [.insert() | upload], something went wrong!', jsonErr);
opts = {file: {}};
}

opts.___s = true;
this._debug(`[FilesCollection] [File Start HTTP] ${opts.file.name || '[no-name]'} - ${opts.fileId}`);
if (helpers.isObject(opts.file) && opts.file.meta) {
opts.file.meta = fixJSONParse(opts.file.meta);
}
if (!helpers.isObject(opts.file)) {
opts.file = {};
}

({result} = this._prepareUpload(helpers.clone(opts), user.userId, 'HTTP Start Method'));
opts.___s = true;
this._debug(`[FilesCollection] [File Start HTTP] ${opts.file.name || '[no-name]'} - ${opts.fileId}`);
if (helpers.isObject(opts.file) && opts.file.meta) {
opts.file.meta = fixJSONParse(opts.file.meta);
}

if (this.collection.findOne(result._id)) {
throw new Meteor.Error(400, 'Can\'t start upload, data substitution detected!');
}
({result} = this._prepareUpload(helpers.clone(opts), user.userId, 'HTTP Start Method'));

opts._id = opts.fileId;
opts.createdAt = new Date();
opts.maxLength = opts.fileLength;
this._preCollection.insert(helpers.omit(opts, '___s'));
this._createStream(result._id, result.path, helpers.omit(opts, '___s'));
if (this.collection.findOne(result._id)) {
throw new Meteor.Error(400, 'Can\'t start upload, data substitution detected!');
}

if (opts.returnMeta) {
if (!httpResp.headersSent) {
httpResp.writeHead(200);
}
opts._id = opts.fileId;
opts.createdAt = new Date();
opts.maxLength = opts.fileLength;
this._preCollection.insert(helpers.omit(opts, '___s'));
this._createStream(result._id, result.path, helpers.omit(opts, '___s'));

if (!httpResp.finished) {
httpResp.end(JSON.stringify({
uploadRoute: `${this.downloadRoute}/${this.collectionName}/__upload`,
file: result
}));
}
} else {
if (!httpResp.headersSent) {
httpResp.writeHead(204);
}
if (opts.returnMeta) {
if (!httpResp.headersSent) {
httpResp.writeHead(200);
}

if (!httpResp.finished) {
httpResp.end();
}
if (!httpResp.finished) {
httpResp.end(JSON.stringify({
uploadRoute: `${this.downloadRoute}/${this.collectionName}/__upload`,
file: result
}));
}
} else {
if (!httpResp.headersSent) {
httpResp.writeHead(204);
}

if (!httpResp.finished) {
httpResp.end();
}
}
} catch (httpRespErr) {
handleError(httpRespErr);
}
}));
} catch (httpRespErr) {
handleError(httpRespErr);
}
};

httpReq.setTimeout(20000, handleError);
if (typeof httpReq.body === 'object' && Object.keys(httpReq.body).length !== 0) {
body = JSON.stringify(httpReq.body);
handleData();
} else {
next();
httpReq.on('data', (data) => bound(() => {
body += data;
}));

httpReq.on('end', () => bound(() => {
handleData();
}));
}
return;
}
Expand All @@ -636,7 +647,7 @@ export class FilesCollection extends FilesCollectionCore {
let uri;

if (!this.public) {
if (!!~httpReq._parsedUrl.path.indexOf(`${this.downloadRoute}/${this.collectionName}`)) {
if (httpReq._parsedUrl.path.includes(`${this.downloadRoute}/${this.collectionName}`)) {
uri = httpReq._parsedUrl.path.replace(`${this.downloadRoute}/${this.collectionName}`, '');
if (uri.indexOf('/') === 0) {
uri = uri.substring(1);
Expand Down Expand Up @@ -666,7 +677,7 @@ export class FilesCollection extends FilesCollectionCore {
next();
}
} else {
if (!!~httpReq._parsedUrl.path.indexOf(`${this.downloadRoute}`)) {
if (httpReq._parsedUrl.path.includes(`${this.downloadRoute}`)) {
uri = httpReq._parsedUrl.path.replace(`${this.downloadRoute}`, '');
if (uri.indexOf('/') === 0) {
uri = uri.substring(1);
Expand All @@ -676,7 +687,7 @@ export class FilesCollection extends FilesCollectionCore {
let _file = uris[uris.length - 1];
if (_file) {
let version;
if (!!~_file.indexOf('-')) {
if (_file.includes('-')) {
version = _file.split('-')[0];
_file = _file.split('-')[1].split('?')[0];
} else {
Expand Down
Loading

0 comments on commit b240418

Please sign in to comment.