Skip to content

Commit

Permalink
fix: retry metadata download if the received JSON is broken
Browse files Browse the repository at this point in the history
close #2949
PR #2971
  • Loading branch information
zkochan committed Nov 8, 2020
1 parent e6ac4f8 commit 5ff6c28
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-scissors-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pnpm/npm-resolver": patch
---

Retry metadata download if the received JSON is broken.
2 changes: 2 additions & 0 deletions packages/npm-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@
"@pnpm/logger": "^3.1.0"
},
"dependencies": {
"@pnpm/core-loggers": "workspace:^5.0.2",
"@pnpm/error": "workspace:1.3.1",
"@pnpm/fetching-types": "workspace:^1.0.0",
"@pnpm/resolve-workspace-range": "workspace:1.0.1",
"@pnpm/resolver-base": "workspace:7.0.5",
"@pnpm/types": "workspace:6.3.1",
"@zkochan/retry": "^0.2.0",
"encode-registry": "^3.0.0",
"load-json-file": "^6.2.0",
"lru-cache": "^6.0.0",
Expand Down
53 changes: 41 additions & 12 deletions packages/npm-resolver/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
import { requestRetryLogger } from '@pnpm/core-loggers'
import PnpmError, {
FetchError,
FetchErrorRequest,
FetchErrorResponse,
} from '@pnpm/error'
import { FetchFromRegistry, RetryTimeoutOptions } from '@pnpm/fetching-types'
import { PackageMeta } from './pickPackage'
import * as retry from '@zkochan/retry'
import url = require('url')

interface RegistryResponse {
Expand Down Expand Up @@ -39,21 +41,48 @@ export class RegistryResponseError extends FetchError {

export default async function fromRegistry (
fetch: FetchFromRegistry,
retry: RetryTimeoutOptions,
retryOpts: RetryTimeoutOptions,
pkgName: string,
registry: string,
authHeaderValue?: string
) {
): Promise<PackageMeta> {
const uri = toUri(pkgName, registry)
const response = await fetch(uri, { authHeaderValue, retry }) as RegistryResponse
if (response.status > 400) {
const request = {
authHeaderValue,
url: uri,
}
throw new RegistryResponseError(request, response, pkgName)
}
return response.json()
const op = retry.operation(retryOpts)
return new Promise((resolve, reject) =>
op.attempt(async (attempt) => {
const response = await fetch(uri, { authHeaderValue, retry: retryOpts }) as RegistryResponse
if (response.status > 400) {
const request = {
authHeaderValue,
url: uri,
}
reject(new RegistryResponseError(request, response, pkgName))
return
}

// Here we only retry broken JSON responses.
// Other HTTP issues are retried by the @pnpm/fetch library
try {
resolve(await response.json())
} catch (error) {
const timeout = op.retry(
new PnpmError('BROKEN_METADATA_JSON', error.message)
)
if (timeout === false) {
reject(op.mainError())
return
}
requestRetryLogger.debug({
attempt,
error,
maxRetries: retryOpts.retries!,
method: 'GET',
timeout,
url: uri,
})
}
})
)
}

function toUri (pkgName: string, registry: string) {
Expand Down
22 changes: 22 additions & 0 deletions packages/npm-resolver/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1427,3 +1427,25 @@ test('resolveFromNpm() should always return the name of the package that is spec
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})

test('request to metadata is retried if the received JSON is broken', async () => {
const registry = 'https://registry1.com/'
nock(registry)
.get('/is-positive')
.reply(200, '{')

nock(registry)
.get('/is-positive')
.reply(200, isPositiveMeta)

const storeDir = tempy.directory()
const resolve = createResolveFromNpm({
retry: { retries: 1 },
storeDir,
})
const resolveResult = await resolve({ alias: 'is-positive', pref: '1.0.0' }, {
registry,
})!

expect(resolveResult?.id).toBe('registry.npmjs.org/is-positive/1.0.0')
})
4 changes: 4 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5ff6c28

Please sign in to comment.