Skip to content

Commit e7afeda

Browse files
fix: do not use excessive memory while parsing packfiles (#5)
1 parent 114b5d3 commit e7afeda

File tree

18 files changed

+883
-423
lines changed

18 files changed

+883
-423
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.github/workflows/test.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,16 @@ jobs:
2424
- run: yarn install --frozen-lockfile
2525
- run: yarn build
2626
- run: yarn test
27+
28+
test-memory-usage:
29+
runs-on: ubuntu-latest
30+
31+
steps:
32+
- uses: actions/checkout@v2
33+
- uses: actions/setup-node@v1
34+
with:
35+
node-version: 16.x
36+
- run: yarn install --frozen-lockfile
37+
- run: yarn build
38+
- name: Check memory usage for packfile parser
39+
run: node scripts/test-limited-memory-parse scripts/packfiles/nodejs-node.dat

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,6 @@ bundle-cache
9696
package-entry-points.json
9797

9898
secrets/
99-
packages/packfile/samples/output.pack
99+
packages/packfile/samples/output.pack
100+
101+
temp/

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM git-client-deps
2+
3+
WORKDIR /app
4+
5+
ADD . /app
6+
7+
CMD node --max_old_space_size=128 scripts/test-parse

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"is-builtin-module": "^3.0.0",
2424
"jest": "^26.0.1",
2525
"lint-staged": "^10.1.3",
26+
"parameter-reducers": "^2.0.0",
2627
"prettier": "^2.0.4",
2728
"react": "^17.0.1",
2829
"react-dom": "^17.0.1",

packages/http/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ After the initial request, all other requests require an object detailing th ser
5151

5252
#### DEFAULT_HTTP_HANDLER
5353

54-
An implementation of `HttpInterface` using `cross-fetch`. You can wrap these if you need to set additional headers (e.g. for auth).
54+
An implementation of `HttpInterface` using `http-basic`. You can wrap these if you need to set additional headers (e.g. for auth).
5555

5656
### Methods
5757

packages/http/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
},
2121
"dependencies": {
2222
"@rollingversions/git-protocol": "^0.0.0",
23-
"cross-fetch": "^3.0.6",
2423
"http-basic": "^8.1.3",
2524
"https-proxy-agent": "^5.0.0"
2625
},

packages/http/src/fetchObjects.ts

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import {
33
parseFetchResponse,
44
FetchCommand,
55
FetchResponseEntryObject,
6+
FetchCommandOutputOptions,
7+
parseFetchResponseV2,
8+
FetchCommandOutputOptionsV2,
69
} from '@rollingversions/git-protocol';
710
import {ContextWithServerCapabilities} from './Context';
811

@@ -16,7 +19,12 @@ export default async function fetchObjects<
1619
>(
1720
repoURL: URL,
1821
command: FetchCommand,
19-
ctx: ContextWithServerCapabilities<THeaders>,
22+
{
23+
raw,
24+
store,
25+
storeMode,
26+
...ctx
27+
}: ContextWithServerCapabilities<THeaders> & FetchCommandOutputOptions,
2028
) {
2129
const url = new URL(
2230
`${
@@ -56,5 +64,64 @@ export default async function fetchObjects<
5664
)}`,
5765
);
5866
}
59-
return parseFetchResponse(response.body);
67+
return parseFetchResponse(response.body, {raw, store, storeMode});
68+
}
69+
70+
export async function* fetchObjectsV2<
71+
THeaders extends {set(name: string, value: string): unknown}
72+
>(
73+
repoURL: URL,
74+
command: FetchCommand,
75+
{
76+
onProgress,
77+
store,
78+
storeMode,
79+
...ctx
80+
}: ContextWithServerCapabilities<THeaders> & FetchCommandOutputOptionsV2,
81+
) {
82+
const url = new URL(
83+
`${
84+
repoURL.href.endsWith('.git') ? repoURL.href : `${repoURL.href}.git`
85+
}/git-upload-pack`,
86+
);
87+
const headers = ctx.http.createHeaders(url);
88+
headers.set('accept', 'application/x-git-upload-pack-result');
89+
headers.set('content-type', 'application/x-git-upload-pack-request');
90+
headers.set('git-protocol', 'version=2');
91+
headers.set('user-agent', ctx.agent);
92+
93+
const response = await ctx.http.post(
94+
url,
95+
headers,
96+
composeFetchCommand(
97+
command,
98+
new Map(
99+
[
100+
['agent', ctx.agent] as const,
101+
...defaultCapabilities,
102+
].filter(([key]) => ctx.serverCapabilities.has(key)),
103+
),
104+
),
105+
);
106+
if (response.statusCode !== 200) {
107+
const body = await new Promise<Buffer>((resolve, reject) => {
108+
const body: Buffer[] = [];
109+
response.body
110+
.on(`data`, (chunk) => body.push(chunk))
111+
.on(`error`, reject)
112+
.on(`end`, () => resolve(Buffer.concat(body)));
113+
});
114+
throw new Error(
115+
`Git server responded with status ${response.statusCode}: ${body.toString(
116+
`utf8`,
117+
)}`,
118+
);
119+
}
120+
for await (const entry of parseFetchResponseV2(response.body, {
121+
onProgress,
122+
store,
123+
storeMode,
124+
})) {
125+
yield entry;
126+
}
60127
}

packages/packfile/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
"build": "tsc"
2020
},
2121
"dependencies": {
22-
"@rollingversions/git-core": "^0.0.0",
23-
"@types/pako": "^1.0.2",
24-
"pako": "^1.0.5"
22+
"@rollingversions/git-core": "^0.0.0"
2523
},
2624
"devDependencies": {
2725
"@types/node": "*"

0 commit comments

Comments
 (0)