-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathipinfoResProxyWrapper.ts
More file actions
128 lines (111 loc) · 3.7 KB
/
ipinfoResProxyWrapper.ts
File metadata and controls
128 lines (111 loc) · 3.7 KB
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import type { RequestInit, Response } from "node-fetch";
import Cache from "./cache/cache";
import LruCache from "./cache/lruCache";
import ApiLimitError from "./errors/apiLimitError";
import { isInSubnet } from "subnet-check";
import {
REQUEST_TIMEOUT_DEFAULT,
CACHE_VSN,
HOST_RES_PROXY,
BOGON_NETWORKS,
IPinfoResProxy,
IPBogon
} from "./common";
import VERSION from "./version";
const clientUserAgent = `IPinfoClient/nodejs/${VERSION}`;
export default class IPinfoResProxyWrapper {
private token: string;
private baseUrl: string;
private cache: Cache;
private timeout: number;
/**
* Creates IPinfoResProxyWrapper object to communicate with the IPinfo Res Proxy API.
*
* @param token Token string provided by IPinfo for the registered user.
* @param cache An implementation of IPCache interface, or LruCache if not specified.
* @param timeout Request timeout in milliseconds, or 5000ms if not specified. 0 disables the timeout.
* @param baseUrl The base url to use for api requests, or "ipinfo.io" if not specified.
*/
constructor(
token: string,
cache?: Cache,
timeout?: number,
baseUrl?: string
) {
this.token = token;
this.cache = cache || new LruCache();
this.timeout =
timeout === null || timeout === undefined
? REQUEST_TIMEOUT_DEFAULT
: timeout;
this.baseUrl = baseUrl || `https://${HOST_RES_PROXY}`;
}
public static cacheKey(k: string): string {
return `${k}:${CACHE_VSN}`;
}
public async fetchApi(
path: string,
init: RequestInit = {}
): Promise<Response> {
const headers = {
Accept: "application/json",
Authorization: `Bearer ${this.token}`,
"Content-Type": "application/json",
"User-Agent": clientUserAgent
};
const request = Object.assign(
{
timeout: this.timeout,
method: "GET",
compress: false
},
init,
{ headers: Object.assign(headers, init.headers) }
);
const url = [this.baseUrl, path].join(
!this.baseUrl.endsWith("/") && !path.startsWith("/") ? "/" : ""
);
return fetch(url, request).then((response: Response) => {
if (response.status === 429) {
throw new ApiLimitError();
}
if (response.status >= 400) {
throw new Error(
`Received an error from the IPinfo API ` +
`(using authorization ${headers["Authorization"]}) ` +
`${response.status} ${response.statusText} ${response.url}`
);
}
return response;
});
}
public async lookupIp(
ip: string | undefined = undefined
): Promise<IPinfoResProxy | IPBogon> {
if (ip && this.isBogon(ip)) {
return { ip, bogon: true };
}
if (!ip) {
ip = "me";
}
const data = await this.cache.get(IPinfoResProxyWrapper.cacheKey(ip));
if (data) {
return data;
}
return this.fetchApi(ip).then(async (response) => {
const ipinfo = (await response.json()) as IPinfoResProxy;
this.cache.set(IPinfoResProxyWrapper.cacheKey(ip), ipinfo);
return ipinfo;
});
}
private isBogon(ip: string): boolean {
if (ip != "") {
for (let network of BOGON_NETWORKS) {
if (isInSubnet(ip, network)) {
return true;
}
}
}
return false;
}
}