apple / apple/app-store-server-library-node

Make urlBase protected so makeFetchRequest overrides work, and add a request timeout

Open
#424 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
382
Forks
79
Avg merge
1d 7h
Merged PRs (30d)
9

Description

> **Edited 2026-08-18.** This issue originally reported `ERR_STREAM_PREMATURE_CLOSE` as a node-fetch chunked-transfer bug. That diagnosis was wrong: the root cause was in Node core and is already fixed. I've rewritten the issue to cover what actually remains. Original context is summarised under "Background" below.

## Request

Two small things I ran into while working around a transport problem in a production service.

### 1. `makeFetchRequest` is `protected`, but `urlBase` is `private`

`makeFetchRequest` (index.ts:292) is `protected`, so subclasses are clearly intended to be able to override the HTTP transport. But `urlBase` (index.ts:203) is `private`, so a subclass cannot read the base URL that method needs.

The result is that any override has to rebuild the URL itself and hardcode the hosts:

```ts
class NativeFetchAppStoreApiClient extends AppStoreServerAPIClient {
private readonly nativeUrlBase: string; // has to duplicate PRODUCTION_URL / SANDBOX_URL

protected async makeFetchRequest(path, parsedQueryParameters, method, requestBody, headers) {
const url = `${this.nativeUrlBase}${path}?${parsedQueryParameters.toString()}`;
...
}
}
```

If those hostnames ever change, subclasses silently keep calling the old ones.

Making `urlBase` `protected` would be a one-line change and would make the existing extension point usable as designed.

This overlaps with #352, which asks for the same seam to attach an outbound proxy agent. Two independent use cases for controlling the transport.

### 2. API requests have no timeout

`makeFetchRequest` issues requests with no timeout, and neither node-fetch nor built-in fetch applies a default. A connection that is accepted but never answered will hang indefinitely rather than failing.

This also looks inconsistent within the project:

- `jws_verification.ts:319` sets `timeout: 30000` for the OCSP request
- the Python library's `_execute_request` passes `timeout=30` on every request

so the API client looks like an oversight rather than a deliberate choice.

## Background

For context on how I hit this — the original report, kept short:

A production service saw `getAllSubscriptionStatuses` fail repeatedly with `ERR_STREAM_PREMATURE_CLOSE` while `getTransactionInfo` succeeded on the same client. We worked around it by subclassing and overriding `makeFetchRequest` to use Node's built-in fetch, which is where I ran into both points above.

The root cause turned out to be in Node, not in this library or in node-fetch's chunked handling. Node 24.17.0 added a response-queue-poisoning guard that attached a public `'data'` listener to idle `http.Agent` sockets. node-fetch v2 tests `socket.listenerCount('data') > 0` to decide whether a response was truncated, counted that guard listener, and reported complete responses as failures. Response size was irrelevant, which is why a status-filtered smaller response still failed for us.

Fixed in Node by https://github.com/nodejs/node/pull/64004, released in 22.23.1, 24.18.0 (both 2026-06-23) and 26.4.0 (2026-06-24). nodejs/node#63989 is closed. I could not reproduce the failure on any current Node release, so I'm not filing this as a bug against this library and I'm not asking for the node-fetch dependency to be changed on account of it.

Happy to open a PR for either point above.

Contributor guide

Open the contributing guide

Research direction

Start in index.ts at urlBase (line 203) and makeFetchRequest (line 292), then compare the existing 30-second OCSP timeout in jws_verification.ts:319 and the Python library's _execute_request behavior. Confirm the intended request-timeout behavior and verify that subclasses can use the base URL without duplicating hosts; done means both changes are covered by tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
api, backend
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.