mirror of https://github.com/Chocobozzz/PeerTube
Add request body limit
parent
539d3f4faa
commit
bfe2ef6bfa
|
@ -1,12 +1,14 @@
|
|||
import * as Bluebird from 'bluebird'
|
||||
import { createWriteStream } from 'fs-extra'
|
||||
import { createWriteStream, remove } from 'fs-extra'
|
||||
import * as request from 'request'
|
||||
import { ACTIVITY_PUB, CONFIG } from '../initializers'
|
||||
import { processImage } from './image-utils'
|
||||
import { join } from 'path'
|
||||
import { logger } from './logger'
|
||||
|
||||
function doRequest <T> (
|
||||
requestOptions: request.CoreOptions & request.UriOptions & { activityPub?: boolean }
|
||||
requestOptions: request.CoreOptions & request.UriOptions & { activityPub?: boolean },
|
||||
bodyKBLimit = 1000 // 1MB
|
||||
): Bluebird<{ response: request.RequestResponse, body: T }> {
|
||||
if (requestOptions.activityPub === true) {
|
||||
if (!Array.isArray(requestOptions.headers)) requestOptions.headers = {}
|
||||
|
@ -15,16 +17,29 @@ function doRequest <T> (
|
|||
|
||||
return new Bluebird<{ response: request.RequestResponse, body: T }>((res, rej) => {
|
||||
request(requestOptions, (err, response, body) => err ? rej(err) : res({ response, body }))
|
||||
.on('data', onRequestDataLengthCheck(bodyKBLimit))
|
||||
})
|
||||
}
|
||||
|
||||
function doRequestAndSaveToFile (requestOptions: request.CoreOptions & request.UriOptions, destPath: string) {
|
||||
function doRequestAndSaveToFile (
|
||||
requestOptions: request.CoreOptions & request.UriOptions,
|
||||
destPath: string,
|
||||
bodyKBLimit = 10000 // 10MB
|
||||
) {
|
||||
return new Bluebird<void>((res, rej) => {
|
||||
const file = createWriteStream(destPath)
|
||||
file.on('finish', () => res())
|
||||
|
||||
request(requestOptions)
|
||||
.on('error', err => rej(err))
|
||||
.on('data', onRequestDataLengthCheck(bodyKBLimit))
|
||||
.on('error', err => {
|
||||
file.close()
|
||||
|
||||
remove(destPath)
|
||||
.catch(err => logger.error('Cannot remove %s after request failure.', destPath, { err }))
|
||||
|
||||
return rej(err)
|
||||
})
|
||||
.pipe(file)
|
||||
})
|
||||
}
|
||||
|
@ -44,3 +59,21 @@ export {
|
|||
doRequestAndSaveToFile,
|
||||
downloadImage
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Thanks to https://github.com/request/request/issues/2470#issuecomment-268929907 <3
|
||||
function onRequestDataLengthCheck (bodyKBLimit: number) {
|
||||
let bufferLength = 0
|
||||
const bytesLimit = bodyKBLimit * 1000
|
||||
|
||||
return function (chunk) {
|
||||
bufferLength += chunk.length
|
||||
if (bufferLength > bytesLimit) {
|
||||
this.abort()
|
||||
|
||||
const error = new Error(`Response was too large - aborted after ${bytesLimit} bytes.`)
|
||||
this.emit('error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,8 @@ function downloadPlaylistSegments (playlistUrl: string, destinationDir: string,
|
|||
for (const fileUrl of fileUrls) {
|
||||
const destPath = join(tmpDirectory, basename(fileUrl))
|
||||
|
||||
await doRequestAndSaveToFile({ uri: fileUrl }, destPath)
|
||||
const bodyKBLimit = 10 * 1000 * 1000 // 10GB
|
||||
await doRequestAndSaveToFile({ uri: fileUrl }, destPath, bodyKBLimit)
|
||||
}
|
||||
|
||||
clearTimeout(timer)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
import './core-utils'
|
||||
import './comment-model'
|
||||
import './request'
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import 'mocha'
|
||||
import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
|
||||
import { get4KFileUrl, root, wait } from '../../../shared/utils'
|
||||
import { join } from 'path'
|
||||
import { pathExists, remove } from 'fs-extra'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('Request helpers', function () {
|
||||
const destPath1 = join(root(), 'test-output-1.txt')
|
||||
const destPath2 = join(root(), 'test-output-2.txt')
|
||||
|
||||
it('Should throw an error when the bytes limit is exceeded for request', async function () {
|
||||
try {
|
||||
await doRequest({ uri: get4KFileUrl() }, 3)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error('No error thrown by do request')
|
||||
})
|
||||
|
||||
it('Should throw an error when the bytes limit is exceeded for request and save file', async function () {
|
||||
try {
|
||||
await doRequestAndSaveToFile({ uri: get4KFileUrl() }, destPath1, 3)
|
||||
} catch {
|
||||
|
||||
await wait(500)
|
||||
expect(await pathExists(destPath1)).to.be.false
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error('No error thrown by do request and save to file')
|
||||
})
|
||||
|
||||
it('Should succeed if the file is below the limit', async function () {
|
||||
await doRequest({ uri: get4KFileUrl() }, 5)
|
||||
await doRequestAndSaveToFile({ uri: get4KFileUrl() }, destPath2, 5)
|
||||
|
||||
expect(await pathExists(destPath2)).to.be.true
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await remove(destPath1)
|
||||
await remove(destPath2)
|
||||
})
|
||||
})
|
|
@ -3,6 +3,10 @@ import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
|
|||
import { isAbsolute, join } from 'path'
|
||||
import { parse } from 'url'
|
||||
|
||||
function get4KFileUrl () {
|
||||
return 'https://download.cpy.re/peertube/4k_file.txt'
|
||||
}
|
||||
|
||||
function makeRawRequest (url: string, statusCodeExpected?: number, range?: string) {
|
||||
const { host, protocol, pathname } = parse(url)
|
||||
|
||||
|
@ -166,6 +170,7 @@ function updateAvatarRequest (options: {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
get4KFileUrl,
|
||||
makeHTMLRequest,
|
||||
makeGetRequest,
|
||||
makeUploadRequest,
|
||||
|
|
Loading…
Reference in New Issue