-
Notifications
You must be signed in to change notification settings - Fork 4
/
RateLimit.js
53 lines (46 loc) · 2.24 KB
/
RateLimit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { catchAsync } from 'source/common/error'
import { createCacheMap } from 'source/common/data/CacheMap'
import { responderEndWithStatusCode } from './Common'
const DEFAULT_RESPONDER_DENY = (store, limitLeft) => responderEndWithStatusCode(store, { statusCode: limitLeft <= 0 ? 429 : 400 })
const DEFAULT_GET_REQUEST_KEY = (store) => `${store.request.socket.remoteAddress}` // limit by ip only
const CACHE_SIZE_SUM_MAX = 32 * 1024 // count, of cacheKey
const CACHE_LIMIT_COUNT = 32 // allowed fail during the expire time
const CACHE_EXPIRE_TIME = 10 * 60 * 1000 // in msec, 10min, time to wait until limitLeft is reset
const GET_DEFAULT_CACHE_MAP = () => createCacheMap({ valueSizeSumMax: CACHE_SIZE_SUM_MAX, eventHub: null })
const createResponderRateLimit = ({ // rate limit for all request, must wait expireTime to reset limitLeft
responderNext,
responderDeny = DEFAULT_RESPONDER_DENY,
getRequestKey = DEFAULT_GET_REQUEST_KEY,
limitCount = CACHE_LIMIT_COUNT,
expireTime = CACHE_EXPIRE_TIME,
rateCacheMap = GET_DEFAULT_CACHE_MAP()
}) => (store) => {
const cacheKey = getRequestKey(store)
let limitLeft = rateCacheMap.get(cacheKey)
if (limitLeft === undefined) limitLeft = limitCount
if (limitLeft <= 0) return responderDeny(store, limitLeft)
rateCacheMap.set(cacheKey, limitLeft - 1, 1, Date.now() + expireTime)
return responderNext(store, limitLeft)
}
const createResponderCheckRateLimit = ({ // only record rate limit for failed check
checkFunc, // return true to pass check
responderNext,
responderDeny = DEFAULT_RESPONDER_DENY,
getRequestKey = DEFAULT_GET_REQUEST_KEY,
limitCount = CACHE_LIMIT_COUNT,
expireTime = CACHE_EXPIRE_TIME,
rateCacheMap = GET_DEFAULT_CACHE_MAP()
}) => async (store) => {
const cacheKey = getRequestKey(store)
let limitLeft = rateCacheMap.get(cacheKey)
if (limitLeft === undefined) limitLeft = limitCount
if (limitLeft <= 0) return responderDeny(store, limitLeft)
const { error, result: isCheckPass } = await catchAsync(checkFunc, store, limitLeft)
if (isCheckPass) return responderNext(store, limitLeft)
rateCacheMap.set(cacheKey, limitLeft - 1, 1, Date.now() + expireTime)
return responderDeny(store, limitLeft, error)
}
export {
createResponderRateLimit,
createResponderCheckRateLimit
}