Skip to content

Commit 4030c48

Browse files
author
Dmitry Shirokov
authored
Merge pull request #7 from runk/reader-reload
Reader reload
2 parents 8802c34 + bd8e1b8 commit 4030c48

File tree

5 files changed

+174
-147
lines changed

5 files changed

+174
-147
lines changed

src/__test__/integration.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,18 @@ describe('getWithPrefixLength', () => {
307307
});
308308
}
309309
});
310+
311+
describe('database reload', () => {
312+
it('works as expected', () => {
313+
const geoIp = open(path.join(dataDir, 'GeoIP2-ISP-Test.mmdb'));
314+
const data = actual('GeoIP2-ISP-Test.json');
315+
316+
const address = '175.16.199.1';
317+
assert.deepStrictEqual(geoIp.get(address), null);
318+
319+
const newDatabase = fs.readFileSync(path.join(dataDir, 'GeoIP2-City-Test.mmdb'))
320+
geoIp.load(newDatabase);
321+
322+
assert.notDeepStrictEqual(geoIp.get(address), null);
323+
})
324+
});

src/index.test.ts

Lines changed: 144 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -3,166 +3,164 @@ import fs from 'fs';
33
import path from 'path';
44
import Reader from '.';
55

6-
describe('reader', () => {
7-
const dataDir = path.join(__dirname, '../test/data/test-data');
8-
const read = (dir: string, filepath: string): Buffer =>
9-
fs.readFileSync(path.join(dir, filepath));
6+
const dataDir = path.join(__dirname, '../test/data/test-data');
7+
const read = (dir: string, filepath: string): Buffer =>
8+
fs.readFileSync(path.join(dir, filepath));
109

11-
describe('findAddressInTree()', () => {
12-
it('should work for most basic case', () => {
13-
const reader: any = new Reader(read(dataDir, 'GeoIP2-City-Test.mmdb'));
14-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [null, 8]);
15-
});
10+
describe('findAddressInTree()', () => {
11+
it('should work for most basic case', () => {
12+
const reader: any = new Reader(read(dataDir, 'GeoIP2-City-Test.mmdb'));
13+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [null, 8]);
14+
});
1615

17-
type treeRecord = [number | null, number];
16+
type treeRecord = [number | null, number];
1817

19-
it('should return correct value: city database', () => {
20-
const reader: any = new Reader(read(dataDir, 'GeoIP2-City-Test.mmdb'));
21-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [null, 8]);
22-
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.1'), [
23-
3383,
24-
24,
25-
]);
26-
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.88'), [
27-
3383,
28-
24,
29-
]);
30-
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.255'), [
31-
3383,
32-
24,
33-
]);
34-
assert.deepStrictEqual(reader.findAddressInTree('::175.16.199.255'), [
35-
3383,
36-
120,
37-
]);
38-
assert.deepStrictEqual(
39-
reader.findAddressInTree('::ffff:175.16.199.255'),
40-
[3383, 120]
41-
);
42-
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf40:ffff::'), [
43-
5114,
44-
29,
45-
]);
46-
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf47:0000::'), [
47-
5114,
48-
29,
49-
]);
50-
assert.deepStrictEqual(
51-
reader.findAddressInTree('2a02:cf47:0000:fff0:ffff::'),
52-
[5114, 29]
53-
);
54-
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf48:0000::'), [
55-
null,
56-
29,
57-
]);
58-
});
18+
it('should return correct value: city database', () => {
19+
const reader: any = new Reader(read(dataDir, 'GeoIP2-City-Test.mmdb'));
20+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [null, 8]);
21+
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.1'), [
22+
3383,
23+
24,
24+
]);
25+
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.88'), [
26+
3383,
27+
24,
28+
]);
29+
assert.deepStrictEqual(reader.findAddressInTree('175.16.199.255'), [
30+
3383,
31+
24,
32+
]);
33+
assert.deepStrictEqual(reader.findAddressInTree('::175.16.199.255'), [
34+
3383,
35+
120,
36+
]);
37+
assert.deepStrictEqual(
38+
reader.findAddressInTree('::ffff:175.16.199.255'),
39+
[3383, 120]
40+
);
41+
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf40:ffff::'), [
42+
5114,
43+
29,
44+
]);
45+
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf47:0000::'), [
46+
5114,
47+
29,
48+
]);
49+
assert.deepStrictEqual(
50+
reader.findAddressInTree('2a02:cf47:0000:fff0:ffff::'),
51+
[5114, 29]
52+
);
53+
assert.deepStrictEqual(reader.findAddressInTree('2a02:cf48:0000::'), [
54+
null,
55+
29,
56+
]);
57+
});
5958

60-
it('should return correct value: string entries', () => {
61-
const reader: any = new Reader(
62-
read(dataDir, 'MaxMind-DB-string-value-entries.mmdb')
63-
);
64-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [225, 32]);
65-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.2'), [214, 31]);
66-
assert.deepStrictEqual(reader.findAddressInTree('175.2.1.1'), [null, 7]);
67-
});
59+
it('should return correct value: string entries', () => {
60+
const reader: any = new Reader(
61+
read(dataDir, 'MaxMind-DB-string-value-entries.mmdb')
62+
);
63+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [225, 32]);
64+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.2'), [214, 31]);
65+
assert.deepStrictEqual(reader.findAddressInTree('175.2.1.1'), [null, 7]);
66+
});
6867

69-
describe('various record sizes and ip versions', () => {
70-
const ips: Record<string, Record<string, treeRecord>> = {
71-
v4: {
72-
'1.1.1.1': [229, 32],
73-
'1.1.1.2': [217, 31],
74-
'1.1.1.32': [241, 32],
75-
'1.1.1.33': [null, 32],
76-
},
77-
v6: {
78-
'::1:ffff:fffa': [null, 126],
79-
'::1:ffff:ffff': [432, 128],
80-
'::2:0000:0000': [450, 122],
81-
'::2:0000:0060': [null, 123],
82-
},
83-
mix: {
84-
'1.1.1.1': [518, 32],
85-
'1.1.1.2': [504, 31],
86-
'1.1.1.32': [532, 32],
87-
'1.1.1.33': [null, 32],
88-
'::1:ffff:fffa': [null, 126],
89-
'::1:ffff:ffff': [547, 128],
90-
'::2:0000:0000': [565, 122],
91-
'::2:0000:0060': [null, 123],
92-
},
93-
};
68+
describe('various record sizes and ip versions', () => {
69+
const ips: Record<string, Record<string, treeRecord>> = {
70+
v4: {
71+
'1.1.1.1': [229, 32],
72+
'1.1.1.2': [217, 31],
73+
'1.1.1.32': [241, 32],
74+
'1.1.1.33': [null, 32],
75+
},
76+
v6: {
77+
'::1:ffff:fffa': [null, 126],
78+
'::1:ffff:ffff': [432, 128],
79+
'::2:0000:0000': [450, 122],
80+
'::2:0000:0060': [null, 123],
81+
},
82+
mix: {
83+
'1.1.1.1': [518, 32],
84+
'1.1.1.2': [504, 31],
85+
'1.1.1.32': [532, 32],
86+
'1.1.1.33': [null, 32],
87+
'::1:ffff:fffa': [null, 126],
88+
'::1:ffff:ffff': [547, 128],
89+
'::2:0000:0000': [565, 122],
90+
'::2:0000:0060': [null, 123],
91+
},
92+
};
9493

95-
interface Scenarios {
96-
[key: string]: Record<string, treeRecord>;
97-
}
94+
interface Scenarios {
95+
[key: string]: Record<string, treeRecord>;
96+
}
9897

99-
const scenarios: Scenarios = {
100-
'MaxMind-DB-test-ipv4-24.mmdb': ips.v4,
101-
'MaxMind-DB-test-ipv4-28.mmdb': ips.v4,
102-
'MaxMind-DB-test-ipv4-32.mmdb': ips.v4,
103-
'MaxMind-DB-test-ipv6-24.mmdb': ips.v6,
104-
'MaxMind-DB-test-ipv6-28.mmdb': ips.v6,
105-
'MaxMind-DB-test-ipv6-32.mmdb': ips.v6,
106-
'MaxMind-DB-test-mixed-24.mmdb': ips.mix,
107-
'MaxMind-DB-test-mixed-28.mmdb': ips.mix,
108-
'MaxMind-DB-test-mixed-32.mmdb': ips.mix,
109-
};
98+
const scenarios: Scenarios = {
99+
'MaxMind-DB-test-ipv4-24.mmdb': ips.v4,
100+
'MaxMind-DB-test-ipv4-28.mmdb': ips.v4,
101+
'MaxMind-DB-test-ipv4-32.mmdb': ips.v4,
102+
'MaxMind-DB-test-ipv6-24.mmdb': ips.v6,
103+
'MaxMind-DB-test-ipv6-28.mmdb': ips.v6,
104+
'MaxMind-DB-test-ipv6-32.mmdb': ips.v6,
105+
'MaxMind-DB-test-mixed-24.mmdb': ips.mix,
106+
'MaxMind-DB-test-mixed-28.mmdb': ips.mix,
107+
'MaxMind-DB-test-mixed-32.mmdb': ips.mix,
108+
};
110109

111-
for (const item in scenarios) {
112-
it('should return correct value: ' + item, () => {
113-
const reader: any = new Reader(read(dataDir, '' + item));
114-
const list = scenarios[item];
115-
for (const ip in list) {
116-
assert.deepStrictEqual(
117-
reader.findAddressInTree(ip),
118-
list[ip],
119-
'IP: ' + ip
120-
);
121-
}
122-
});
123-
}
110+
for (const item in scenarios) {
111+
it('should return correct value: ' + item, () => {
112+
const reader: any = new Reader(read(dataDir, '' + item));
113+
const list = scenarios[item];
114+
for (const ip in list) {
115+
assert.deepStrictEqual(
116+
reader.findAddressInTree(ip),
117+
list[ip],
118+
'IP: ' + ip
119+
);
120+
}
121+
});
122+
}
123+
});
124+
125+
describe('broken files and search trees', () => {
126+
it('should behave fine when there is no ipv4 search tree', () => {
127+
const reader: any = new Reader(
128+
read(dataDir, 'MaxMind-DB-no-ipv4-search-tree.mmdb')
129+
);
130+
assert.deepStrictEqual(reader.findAddressInTree('::1:ffff:ffff'), [
131+
80,
132+
64,
133+
]);
134+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [80, 0]);
124135
});
125136

126-
describe('broken files and search trees', () => {
127-
it('should behave fine when there is no ipv4 search tree', () => {
128-
const reader: any = new Reader(
129-
read(dataDir, 'MaxMind-DB-no-ipv4-search-tree.mmdb')
130-
);
131-
assert.deepStrictEqual(reader.findAddressInTree('::1:ffff:ffff'), [
132-
80,
133-
64,
134-
]);
135-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [80, 0]);
136-
});
137+
it('should behave fine when search tree is broken', () => {
138+
// TODO: find out in what way the file is broken
139+
const reader: any = new Reader(
140+
read(dataDir, 'MaxMind-DB-test-broken-search-tree-24.mmdb')
141+
);
142+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [229, 32]);
143+
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.2'), [217, 31]);
144+
});
145+
});
137146

138-
it('should behave fine when search tree is broken', () => {
139-
// TODO: find out in what way the file is broken
140-
const reader: any = new Reader(
141-
read(dataDir, 'MaxMind-DB-test-broken-search-tree-24.mmdb')
147+
describe('invalid database format', () => {
148+
it('should provide meaningful message when one tries to use legacy db', () => {
149+
assert.throws(() => {
150+
// tslint:disable-next-line: no-unused-expression
151+
new Reader(
152+
read(path.join(__dirname, '../test/databases'), 'legacy.dat')
142153
);
143-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.1'), [229, 32]);
144-
assert.deepStrictEqual(reader.findAddressInTree('1.1.1.2'), [217, 31]);
145-
});
154+
}, /Maxmind v2 module has changed API/);
146155
});
147156

148-
describe('invalid database format', () => {
149-
it('should provide meaningful message when one tries to use legacy db', () => {
150-
assert.throws(() => {
151-
// tslint:disable-next-line: no-unused-expression
152-
new Reader(
153-
read(path.join(__dirname, '../test/databases'), 'legacy.dat')
154-
);
155-
}, /Maxmind v2 module has changed API/);
156-
});
157-
158-
it('should provide meaningful message when one tries to use unknown format', () => {
159-
assert.throws(() => {
160-
// tslint:disable-next-line: no-unused-expression
161-
new Reader(
162-
read(path.join(__dirname, '../test/databases'), 'broken.dat')
163-
);
164-
}, /Cannot parse binary database/);
165-
});
157+
it('should provide meaningful message when one tries to use unknown format', () => {
158+
assert.throws(() => {
159+
// tslint:disable-next-line: no-unused-expression
160+
new Reader(
161+
read(path.join(__dirname, '../test/databases'), 'broken.dat')
162+
);
163+
}, /Cannot parse binary database/);
166164
});
167165
});
168166
});

src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ export default class Reader<T extends Response> {
1313
private db: Buffer;
1414
private ipv4StartNodeNumber: number;
1515
private walker: Walker;
16+
private opts: ReaderOptions;
1617

1718
constructor(db: Buffer, opts: ReaderOptions = {}) {
19+
this.opts = opts;
20+
this.load(db);
21+
}
22+
23+
public load(db: Buffer) {
1824
this.db = db;
1925
this.metadata = parseMetadata(this.db);
2026
this.decoder = new Decoder(
2127
this.db,
2228
this.metadata.searchTreeSize + DATA_SECTION_SEPARATOR_SIZE,
23-
opts.cache
29+
this.opts.cache
2430
);
2531
this.walker = walker(this.db, this.metadata.recordSize);
2632
this.ipv4StartNodeNumber = this.ipv4Start();

test/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Test data
2+
3+
Test data is installed via _git submodule_ from https://github.com/maxmind/MaxMind-DB.git. Execute following command in the project root:
4+
5+
```shell
6+
git submodule update --init
7+
```

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"removeComments": true,
1515
"sourceMap": true,
1616
"strict": true,
17+
"strictPropertyInitialization": false,
1718
"lib": ["ES2019"],
1819
"target": "ES2019"
1920
},

0 commit comments

Comments
 (0)