optimization: avoid geo-querying local IPs
Massimo Melina committed
Jan 5, 2024 at 10:57 UTC
db00cc3e492fbd163c2823dcc9771013346201d0
6 files changed
+16
-10
admin/src/OptionsPage.ts
+2
-2
@@ -6,7 +6,7 @@ import { apiCall, useApiEx } from './api'
6
import { state, useSnapState } from './state'
7
import { Link as RouterLink } from 'react-router-dom'
8
import { CardMembership, EditNote, Refresh, Warning } from '@mui/icons-material'
9
-import { Dict, MAX_TILE_SIZE, REPO_URL, ipLocalHost, wait, with_, try_, ipForUrl, SORT_BY_OPTIONS, THEME_OPTIONS,
9
+import { Dict, MAX_TILE_SIZE, REPO_URL, isIpLocalHost, wait, with_, try_, ipForUrl, SORT_BY_OPTIONS, THEME_OPTIONS,
10
CFG } from './misc'
11
import { iconTooltip, InLink, LinkBtn, modifiedSx, wikiLink, useBreakpoint } from './mui'
12
import { Form, BoolField, NumberField, SelectField, FieldProps, Field, StringField } from '@hfs/mui-grid-form';
@@ -244,7 +244,7 @@ export default function OptionsPage() {
244
if (onHttps && certChange && !await confirmDialog("You may disrupt https service, kicking you out"))
245
return
246
await apiCall('set_config', { values: changes })
247
- if (newPort !== undefined || changes.listen_interface && !(loc.hostname === 'localhost' && ipLocalHost(changes.listen_interface))) {
247
+ if (newPort !== undefined || changes.listen_interface && !(loc.hostname === 'localhost' && isIpLocalHost(changes.listen_interface))) {
248
await alertDialog("You are being redirected but in some cases this may fail. Hold on tight!", 'warning')
249
const host = ipForUrl(changes.listen_interface || loc.hostname)
250
// we have to jump protocol also in case of random port, because we want people to know their port while using GUI
admin/src/mui.ts
+3
-2
@@ -6,7 +6,7 @@ import { SxProps } from '@mui/system'
6
import { createElement as h, FC, forwardRef, Fragment, ReactNode, useCallback, useState } from 'react'
7
import { Box, BoxProps, Breakpoint, ButtonProps, CircularProgress, IconButton, IconButtonProps, Link, LinkProps,
8
Tooltip, TooltipProps, useMediaQuery } from '@mui/material'
9
-import { formatPerc, prefix, WIKI_URL } from '../../src/cross'
9
+import { formatPerc, isIpLan, isIpLocalHost, prefix, WIKI_URL } from '../../src/cross'
10
import { dontBotherWithKeys, useBatch, useStateMounted } from '@hfs/shared'
11
import { Promisable } from '@hfs/mui-grid-form'
12
import { alertDialog, confirmDialog, toast } from './dialog'
@@ -229,7 +229,8 @@ export function useToggleButton(iconBtn: (state:boolean) => Omit<IconBtnProps, '
229
}
230
231
export function Country({ code, ip, def, long, short }: { code: string, ip?: string, def?: ReactNode, long?: boolean, short?: boolean }) {
232
- const { data } = useBatch(code === undefined && ip && ip2countryBatch, ip, { delay: 100 }) // query if necessary
232
+ const good = ip && !isIpLocalHost(ip) && !isIpLan(ip)
233
+ const { data } = useBatch(code === undefined && good && ip2countryBatch, ip, { delay: 100 }) // query if necessary
234
code ||= data || ''
235
const country = code && _.find(COUNTRIES, { code })
236
return !country ? h(Fragment, {}, def) : h(Tooltip, {
plugins/download-counter/plugin.js
+3
-3
@@ -1,7 +1,7 @@
1
// other plugins can use ctx.state.download_counter_ignore to mark downloads that shouldn't be counted
2
3
exports.description = "Counts downloads for each file, and displays the total in the list or file menu"
4
-exports.version = 5.1 // count files in archive
4
+exports.version = 5.11 // change in API
5
exports.apiRequired = 8.3
6
7
exports.config = {
@@ -46,10 +46,10 @@ exports.init = async api => {
46
unload: () => save.flush(), // we may have pending savings
47
middleware: ctx => () => { // callback = execute after other middlewares are done
48
if (ctx.status >= 300 || ctx.state.download_counter_ignore || ctx.state.considerAsGui || ctx.state.includesLastByte === false) return
49
- if (!(ctx.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
49
+ if (!(ctx.state.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
50
ctx.state.completed.then(() => {
51
const key = uri2key(ctx.path)
52
- const entries = ctx.vfsNode ? [key]
52
+ const entries = ctx.state.vfsNode ? [key]
53
: ctx.state.originalStream?.getArchiveEntries?.().filter(x => x.at(-1) !== '/').map(x => key + uri2key(x))
54
if (!entries) return
55
for (const k of entries)
src/apiMiddleware.ts
+1
@@ -91,6 +91,7 @@ export class SendListReadable<T> extends Readable {
91
if (!bufferTime)
92
bufferTime = 200
93
this.processBuffer = _.debounce(() => {
94
+ if (!this.buffer.length) return
95
this.push(this.buffer)
96
this.buffer = []
97
}, bufferTime, { maxWait: bufferTime })
src/cross.ts
+5
-1
@@ -356,10 +356,14 @@ export function xlate(input: any, table: Record<string, any>) {
356
return table[input] ?? input
357
}
358
359
-export function ipLocalHost(ip: string) {
359
+export function isIpLocalHost(ip: string) {
360
return ip === '::1' || ip.endsWith('127.0.0.1')
361
}
362
363
+export function isIpLan(ip: string) {
364
+ return /^(?:|:10\..*|172\.(1[6-9]|2\d|3[01])\..*|192\.168\..*)$/.test(ip)
365
+}
366
+
367
export function ipForUrl(ip: string) {
368
return ip.includes(':') ? '[' + ip + ']' : ip
369
}
src/misc.ts
+2
-2
@@ -14,7 +14,7 @@ import { Readable } from 'stream'
14
import { SocketAddress, BlockList } from 'node:net'
15
import { ApiError } from './apiMiddleware'
16
import { HTTP_BAD_REQUEST } from './const'
17
-import { ipLocalHost, makeMatcher } from './cross'
17
+import { isIpLocalHost, makeMatcher } from './cross'
18
import { isIPv6 } from 'net'
19
20
type ProcessExitHandler = (signal:string) => any
@@ -58,7 +58,7 @@ export function onOff(em: EventEmitter, events: { [eventName:string]: (...args:
58
59
export function isLocalHost(c: Connection | Koa.Context | string) {
60
const ip = typeof c === 'string' ? c : c.socket.remoteAddress // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
61
- return ip && ipLocalHost(ip)
61
+ return ip && isIpLocalHost(ip)
62
}
63
64
export function makeNetMatcher(mask: string, emptyMaskReturns=false) {