Skip to content

Commit 2e40d87

Browse files
authored
Potential fix for range request beyond end of file (#23)
1 parent 6b7e09f commit 2e40d87

File tree

7 files changed

+1096
-147
lines changed

7 files changed

+1096
-147
lines changed

eslint.config.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import tseslint from 'typescript-eslint'
66

77
export default defineConfig(
88
{
9-
ignores: ['esm/**/*', 'dist/**/*', '*.js', '*.mjs', 'example/*'],
9+
ignores: [
10+
'esm/**/*',
11+
'dist/**/*',
12+
'*.js',
13+
'*.mjs',
14+
'example/*',
15+
'test/browser.test.ts',
16+
],
1017
},
1118
{
1219
languageOptions: {

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,19 @@
4141
"author": "Matt Morgan",
4242
"repository": "GMOD/trix-js",
4343
"devDependencies": {
44-
"@types/node": "^25.0.9",
45-
"@typescript-eslint/eslint-plugin": "^8.53.0",
46-
"@typescript-eslint/parser": "^8.53.0",
44+
"@types/node": "^25.0.10",
45+
"puppeteer": "^24.0.0",
46+
"@typescript-eslint/eslint-plugin": "^8.53.1",
47+
"@typescript-eslint/parser": "^8.53.1",
4748
"eslint": "^9.0.0",
4849
"eslint-plugin-import": "^2.31.0",
4950
"eslint-plugin-unicorn": "^62.0.0",
5051
"generic-filehandle2": "^2.0.18",
51-
"prettier": "^3.8.0",
52+
"prettier": "^3.8.1",
5253
"rimraf": "^6.0.1",
5354
"typescript": "^5.7.0",
54-
"typescript-eslint": "^8.53.0",
55-
"vitest": "^4.0.17"
55+
"typescript-eslint": "^8.53.1",
56+
"vitest": "^4.0.18"
5657
},
5758
"publishConfig": {
5859
"access": "public"

src/index.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,28 @@ const ADDRESS_SIZE = 10
1212
export default class Trix {
1313
private decoder = new TextDecoder('utf8')
1414
private indexCache?: readonly (readonly [string, number])[]
15+
private ixFileSize?: number
1516

1617
constructor(
1718
public ixxFile: GenericFilehandle,
1819
public ixFile: GenericFilehandle,
1920
public maxResults = 20,
2021
) {}
2122

23+
private async getIxFileSize(opts?: { signal?: AbortSignal }) {
24+
if (this.ixFileSize !== undefined) {
25+
return this.ixFileSize
26+
}
27+
try {
28+
// @ts-expect-error
29+
const stat = await this.ixFile.stat(opts)
30+
this.ixFileSize = stat.size
31+
return this.ixFileSize
32+
} catch {
33+
return undefined
34+
}
35+
}
36+
2237
async search(searchString: string, opts?: { signal?: AbortSignal }) {
2338
let resultArr = [] as [string, string][]
2439
const searchWords = searchString.split(' ')
@@ -30,6 +45,7 @@ export default class Trix {
3045
const res = await this.getBuffer(searchWord, opts)
3146

3247
let { end, buffer } = res
48+
const { fileSize } = res
3349
let done = false
3450
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
3551
while (!done) {
@@ -68,13 +84,22 @@ export default class Trix {
6884
break
6985
}
7086

71-
// fetch more data
72-
const res2 = await this.ixFile.read(CHUNK_SIZE, end, opts)
87+
// avoid reading past end of file
88+
if (fileSize !== undefined && end >= fileSize) {
89+
break
90+
}
91+
92+
// fetch more data, clamping to file size if known
93+
let bytesToRead = CHUNK_SIZE
94+
if (fileSize !== undefined) {
95+
bytesToRead = Math.min(CHUNK_SIZE, fileSize - end)
96+
}
97+
const res2 = await this.ixFile.read(bytesToRead, end, opts)
7398
if (res2.length === 0) {
7499
break
75100
}
76101
buffer = concatUint8Array([buffer, res2])
77-
end += CHUNK_SIZE
102+
end += res2.length
78103
}
79104
}
80105

@@ -116,7 +141,12 @@ export default class Trix {
116141
}
117142
}
118143

144+
const fileSize = await this.getIxFileSize(opts)
145+
if (fileSize !== undefined) {
146+
end = Math.min(end, fileSize)
147+
}
148+
119149
const buffer = await this.ixFile.read(end - start, start, opts)
120-
return { buffer, end }
150+
return { buffer, end, fileSize }
121151
}
122152
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Browser tests with Puppeteer > handles EOF correctly with CORS enabled server 1`] = `
4+
[
5+
[
6+
"this",
7+
"id1",
8+
],
9+
[
10+
"this",
11+
"id2",
12+
],
13+
[
14+
"this",
15+
"id3",
16+
],
17+
]
18+
`;
19+
20+
exports[`Browser tests with Puppeteer > searches via HTTP with CORS enabled server 1`] = `
21+
[
22+
[
23+
"for",
24+
"id1",
25+
],
26+
[
27+
"for",
28+
"id2",
29+
],
30+
[
31+
"for",
32+
"id3",
33+
],
34+
]
35+
`;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`HTTP server tests with CORS > handles search near end of file without reading past EOF with CORS 1`] = `
4+
[
5+
[
6+
"this",
7+
"id1",
8+
],
9+
[
10+
"this",
11+
"id2",
12+
],
13+
[
14+
"this",
15+
"id3",
16+
],
17+
]
18+
`;
19+
20+
exports[`HTTP server tests with CORS > searches via HTTP with CORS 1`] = `
21+
[
22+
[
23+
"for",
24+
"id1",
25+
],
26+
[
27+
"for",
28+
"id2",
29+
],
30+
[
31+
"for",
32+
"id3",
33+
],
34+
]
35+
`;
36+
37+
exports[`HTTP server tests without CORS > handles search near end of file without reading past EOF 1`] = `
38+
[
39+
[
40+
"this",
41+
"id1",
42+
],
43+
[
44+
"this",
45+
"id2",
46+
],
47+
[
48+
"this",
49+
"id3",
50+
],
51+
]
52+
`;
53+
54+
exports[`HTTP server tests without CORS > searches via HTTP without CORS 1`] = `
55+
[
56+
[
57+
"for",
58+
"id1",
59+
],
60+
[
61+
"for",
62+
"id2",
63+
],
64+
[
65+
"for",
66+
"id3",
67+
],
68+
]
69+
`;

0 commit comments

Comments
 (0)