diff options
Diffstat (limited to 'packages/backend/src')
| -rw-r--r-- | packages/backend/src/core/DownloadService.ts | 34 | ||||
| -rw-r--r-- | packages/backend/src/core/HttpRequestService.ts | 90 | ||||
| -rw-r--r-- | packages/backend/src/core/activitypub/ApRequestService.ts | 17 | ||||
| -rw-r--r-- | packages/backend/src/core/activitypub/ApResolverService.ts | 10 | ||||
| -rw-r--r-- | packages/backend/src/misc/dev-null.ts | 11 | ||||
| -rw-r--r-- | packages/backend/src/server/web/views/base.pug | 2 |
6 files changed, 103 insertions, 61 deletions
diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index a3078bff45..c5b2bcaef4 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -4,8 +4,8 @@ import * as util from 'node:util'; import { Inject, Injectable } from '@nestjs/common'; import IPCIDR from 'ip-cidr'; import PrivateIp from 'private-ip'; -import got, * as Got from 'got'; import chalk from 'chalk'; +import { buildConnector } from 'undici'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js'; @@ -13,7 +13,6 @@ import { createTemp } from '@/misc/create-temp.js'; import { StatusError } from '@/misc/status-error.js'; import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; -import { buildConnector } from 'undici'; const pipeline = util.promisify(stream.pipeline); import { bindThis } from '@/decorators.js'; @@ -32,23 +31,20 @@ export class DownloadService { ) { this.logger = this.loggerService.getLogger('download'); - this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption( - { - 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); + 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 diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 2864ad4405..cd859d0023 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -1,14 +1,14 @@ import * as http from 'node:http'; import * as https from 'node:https'; +import { LookupFunction } from 'node:net'; import CacheableLookup from 'cacheable-lookup'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { Inject, Injectable } from '@nestjs/common'; +import * as undici from 'undici'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { StatusError } from '@/misc/status-error.js'; import { bindThis } from '@/decorators.js'; -import * as undici from 'undici'; -import { LookupFunction } from 'node:net'; import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; @@ -62,7 +62,7 @@ export class UndiciFetcher { undici.buildConnector(args.agentOptions.connect as undici.buildConnector.BuildOptions)(options, (err, socket) => { this.logger?.debug('Socket connector called', socket); if (err) { - this.logger?.debug(`Socket error`, err); + this.logger?.debug('Socket error', err); cb(new Error(`Error while socket connecting\n${err}`), null); return; } @@ -79,20 +79,20 @@ export class UndiciFetcher { uri: args.proxy.uri, - connect: (process.env.NODE_ENV !== 'production' && typeof (args.proxy?.options?.connect ?? args.agentOptions.connect) !== 'function') + connect: (process.env.NODE_ENV !== 'production' && typeof (args.proxy.options?.connect ?? args.agentOptions.connect) !== 'function') ? (options, cb) => { // Custom connector for debug undici.buildConnector((args.proxy?.options?.connect ?? args.agentOptions.connect) as undici.buildConnector.BuildOptions)(options, (err, socket) => { this.logger?.debug('Socket connector called (secure)', socket); if (err) { - this.logger?.debug(`Socket error`, err); + this.logger?.debug('Socket error', err); cb(new Error(`Error while socket connecting\n${err}`), null); return; } this.logger?.debug(`Socket connected (secure): port ${socket.localPort} => remote ${socket.remoteAddress}`); cb(null, socket); }); - } : (args.proxy?.options?.connect ?? args.agentOptions.connect), + } : (args.proxy.options?.connect ?? args.agentOptions.connect), }) : this.nonProxiedAgent; } @@ -115,7 +115,7 @@ export class UndiciFetcher { public async fetch( url: string | URL, options: undici.RequestInit = {}, - privateOptions: { noOkError?: boolean; bypassProxy?: boolean; } = { noOkError: false, bypassProxy: false } + privateOptions: { noOkError?: boolean; bypassProxy?: boolean; } = { noOkError: false, bypassProxy: false }, ): Promise<undici.Response> { const res = await undici.fetch(url, { dispatcher: this.getAgentByUrl(new URL(url), privateOptions.bypassProxy), @@ -135,31 +135,56 @@ export class UndiciFetcher { } @bindThis + public async request( + url: string | URL, + options: { dispatcher?: undici.Dispatcher } & Omit<undici.Dispatcher.RequestOptions, 'origin' | 'path' | 'method'> & Partial<Pick<undici.Dispatcher.RequestOptions, 'method'>> = {}, + privateOptions: { noOkError?: boolean; bypassProxy?: boolean; } = { noOkError: false, bypassProxy: false }, + ): Promise<undici.Dispatcher.ResponseData> { + const res = await undici.request(url, { + dispatcher: this.getAgentByUrl(new URL(url), privateOptions.bypassProxy), + ...options, + headers: { + 'user-agent': this.userAgent ?? '', + ...(options.headers ?? {}), + }, + }).catch((err) => { + this.logger?.error(`fetch error to ${typeof url === 'string' ? url : url.href}`, err); + throw new StatusError('Resource Unreachable', 500, 'Resource Unreachable'); + }); + + if (res.statusCode >= 400) { + throw new StatusError(`${res.statusCode}`, res.statusCode, ''); + } + + return res; + } + + @bindThis public async getJson<T extends unknown>(url: string, accept = 'application/json, */*', headers?: Record<string, string>): Promise<T> { - const res = await this.fetch( + const { body } = await this.request( url, { headers: Object.assign({ Accept: accept, }, headers ?? {}), - } + }, ); - return await res.json() as T; + return await body.json() as T; } @bindThis public async getHtml(url: string, accept = 'text/html, */*', headers?: Record<string, string>): Promise<string> { - const res = await this.fetch( + const { body } = await this.request( url, { headers: Object.assign({ Accept: accept, }, headers ?? {}), - } + }, ); - return await res.text(); + return await body.text(); } } @@ -167,6 +192,7 @@ export class UndiciFetcher { export class HttpRequestService { public defaultFetcher: UndiciFetcher; public fetch: UndiciFetcher['fetch']; + public request: UndiciFetcher['request']; public getHtml: UndiciFetcher['getHtml']; public defaultJsonFetcher: UndiciFetcher; public getJson: UndiciFetcher['getJson']; @@ -219,18 +245,19 @@ export class HttpRequestService { maxCachedSessions: 300, // TLSセッションのキャッシュ数 https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L80 lookup: this.dnsCache.lookup as LookupFunction, // https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L98 }, - } + }; - this.maxSockets = Math.max(64, this.config.deliverJobConcurrency ?? 128); + this.maxSockets = Math.max(64, ((this.config.deliverJobConcurrency ?? 128) / (this.config.clusterLimit ?? 1))); - this.defaultFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption(), this.logger); + this.defaultFetcher = this.createFetcher({}, {}, this.logger); this.fetch = this.defaultFetcher.fetch; + this.request = this.defaultFetcher.request; this.getHtml = this.defaultFetcher.getHtml; - this.defaultJsonFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption({ + this.defaultJsonFetcher = this.createFetcher({ maxResponseSize: 1024 * 256, - }), this.logger); + }, {}, this.logger); this.getJson = this.defaultJsonFetcher.getJson; @@ -272,23 +299,28 @@ export class HttpRequestService { } @bindThis - public getStandardUndiciFetcherOption(opts: undici.Agent.Options = {}, proxyOpts: undici.Agent.Options = {}) { + private getStandardUndiciFetcherOption(opts: undici.Agent.Options = {}, proxyOpts: undici.Agent.Options = {}) { return { agentOptions: { ...this.clientDefaults, ...opts, }, ...(this.config.proxy ? { - proxy: { - uri: this.config.proxy, - options: { - connections: this.maxSockets, - ...proxyOpts, - } - } + proxy: { + uri: this.config.proxy, + options: { + connections: this.maxSockets, + ...proxyOpts, + }, + }, } : {}), userAgent: this.config.userAgent, - } + }; + } + + @bindThis + public createFetcher(opts: undici.Agent.Options = {}, proxyOpts: undici.Agent.Options = {}, logger: Logger) { + return new UndiciFetcher(this.getStandardUndiciFetcherOption(opts, proxyOpts), logger); } /** @@ -314,13 +346,13 @@ export class HttpRequestService { connector(options, (err, socket) => { this.logger.debug('Socket connector (with ip checker) called', socket); if (err) { - this.logger.error(`Socket error`, err) + this.logger.error('Socket error', err); cb(new Error(`Error while socket connecting\n${err}`), null); return; } if (socket.remoteAddress == undefined) { - this.logger.error(`Socket error: remoteAddress is undefined`); + this.logger.error('Socket error: remoteAddress is undefined'); cb(new Error('remoteAddress is undefined (maybe socket destroyed)'), null); return; } diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index ab22a0c411..db87475c4c 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -9,10 +9,12 @@ import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js' import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import type Logger from '@/logger.js'; +import type { Dispatcher } from 'undici'; +import { DevNull } from '@/misc/dev-null.js'; type Request = { url: string; - method: string; + method: Dispatcher.HttpMethod; headers: Record<string, string>; }; @@ -41,10 +43,10 @@ export class ApRequestService { private httpRequestService: HttpRequestService, private loggerService: LoggerService, ) { - this.logger = this.loggerService?.getLogger('ap-request'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる - this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({ + this.logger = this.loggerService.getLogger('ap-request'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる + this.undiciFetcher = this.httpRequestService.createFetcher({ maxRedirections: 0, - }), this.logger ); + }, {}, this.logger); } @bindThis @@ -163,14 +165,15 @@ export class ApRequestService { }, }); - await this.undiciFetcher.fetch( + const response = await this.undiciFetcher.request( url, { method: req.request.method, headers: req.request.headers, body, - } + }, ); + response.body.pipe(new DevNull()); } /** @@ -197,7 +200,7 @@ export class ApRequestService { { method: req.request.method, headers: req.request.headers, - } + }, ); return await res.json(); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index e51ae37954..ca7760af81 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -8,13 +8,13 @@ import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js' import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import type Logger from '@/logger.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; -import { LoggerService } from '@/core/LoggerService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -import type Logger from '@/logger.js'; export class Resolver { private history: Set<string>; @@ -39,10 +39,10 @@ export class Resolver { private recursionLimit = 100, ) { this.history = new Set(); - this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる - this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({ + this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる + this.undiciFetcher = this.httpRequestService.createFetcher({ maxRedirections: 0, - }), this.logger); + }, {}, this.logger); } @bindThis diff --git a/packages/backend/src/misc/dev-null.ts b/packages/backend/src/misc/dev-null.ts new file mode 100644 index 0000000000..38b9d82669 --- /dev/null +++ b/packages/backend/src/misc/dev-null.ts @@ -0,0 +1,11 @@ +import { Writable, WritableOptions } from "node:stream"; + +export class DevNull extends Writable implements NodeJS.WritableStream { + constructor(opts?: WritableOptions) { + super(opts); + } + + _write (chunk: any, encoding: BufferEncoding, cb: (err?: Error | null) => void) { + setImmediate(cb); + } +} diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index b27bbcbce0..d05901baec 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -35,7 +35,7 @@ html link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') - link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.css') + link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css') link(rel='modulepreload' href=`/vite/${clientEntry.file}`) if !config.clientManifestExists |