diff options
Diffstat (limited to 'packages/backend/src/core/DownloadService.ts')
| -rw-r--r-- | packages/backend/src/core/DownloadService.ts | 76 |
1 files changed, 53 insertions, 23 deletions
diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index c5b2bcaef4..a971e06fd8 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -5,10 +5,10 @@ import { Inject, Injectable } from '@nestjs/common'; import IPCIDR from 'ip-cidr'; import PrivateIp from 'private-ip'; import chalk from 'chalk'; -import { buildConnector } from 'undici'; +import got, * as Got from 'got'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; import { createTemp } from '@/misc/create-temp.js'; import { StatusError } from '@/misc/status-error.js'; import { LoggerService } from '@/core/LoggerService.js'; @@ -20,7 +20,6 @@ import { bindThis } from '@/decorators.js'; @Injectable() export class DownloadService { private logger: Logger; - private undiciFetcher: UndiciFetcher; constructor( @Inject(DI.config) @@ -30,21 +29,6 @@ export class DownloadService { private loggerService: LoggerService, ) { this.logger = this.loggerService.getLogger('download'); - - this.undiciFetcher = this.httpRequestService.createFetcher({ - connect: process.env.NODE_ENV === 'development' ? - this.httpRequestService.clientDefaults.connect - : - this.httpRequestService.getConnectorWithIpCheck( - buildConnector({ - ...this.httpRequestService.clientDefaults.connect, - }), - (ip) => !this.isPrivateIp(ip), - ), - bodyTimeout: 30 * 1000, - }, { - connect: this.httpRequestService.clientDefaults.connect, - }, this.logger); } @bindThis @@ -55,13 +39,59 @@ export class DownloadService { const operationTimeout = 60 * 1000; const maxSize = this.config.maxFileSize ?? 262144000; - const response = await this.undiciFetcher.fetch(url); + const req = got.stream(url, { + headers: { + 'User-Agent': this.config.userAgent, + }, + timeout: { + lookup: timeout, + connect: timeout, + secureConnect: timeout, + socket: timeout, // read timeout + response: timeout, + send: timeout, + request: operationTimeout, // whole operation timeout + }, + agent: { + http: this.httpRequestService.httpAgent, + https: this.httpRequestService.httpsAgent, + }, + http2: false, // default + retry: { + limit: 0, + }, + }).on('response', (res: Got.Response) => { + if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) { + if (this.isPrivateIp(res.ip)) { + this.logger.warn(`Blocked address: ${res.ip}`); + req.destroy(); + } + } - if (response.body === null) { - throw new StatusError('No body', 400, 'No body'); - } + const contentLength = res.headers['content-length']; + if (contentLength != null) { + const size = Number(contentLength); + if (size > maxSize) { + this.logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`); + req.destroy(); + } + } + }).on('downloadProgress', (progress: Got.Progress) => { + if (progress.transferred > maxSize) { + this.logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); + req.destroy(); + } + }); - await pipeline(stream.Readable.fromWeb(response.body), fs.createWriteStream(path)); + try { + await pipeline(req, fs.createWriteStream(path)); + } catch (e) { + if (e instanceof Got.HTTPError) { + throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage); + } else { + throw e; + } + } this.logger.succ(`Download finished: ${chalk.cyan(url)}`); } |