@mark1russell7/service 0.5.11 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc.js","names":["SERVING","knob","t"],"sources":["../src/grpc/connection.ts","../src/grpc/meets.ts","../src/grpc/client.ts","../src/grpc/machine.ts","../src/grpc/starting.ts"],"sourcesContent":["import type { Factory } from \"../client/core.ts\";\n\n/** A state is the connectivity state of a channel of grpc-js, by the names of its enum `connectivityState`. */\nexport type GrpcState = \"IDLE\" | \"CONNECTING\" | \"READY\" | \"TRANSIENT_FAILURE\" | \"SHUTDOWN\";\n\n/** These states are in the sequence of the enum of grpc-js: the index of a state is its value. */\nexport const GRPC_STATES: readonly GrpcState[] = [\"IDLE\", \"CONNECTING\", \"READY\", \"TRANSIENT_FAILURE\", \"SHUTDOWN\"];\n\n/** A status is the end of one call: its code (0 is OK) and its details. */\nexport interface GrpcStatus {\n readonly code: number;\n readonly details: string;\n}\n\n/** A call handle stops one call in flight. The grpc-js driver then writes RST_STREAM with the code CANCEL. */\nexport interface GrpcCallHandle {\n cancel(): void;\n}\n\n/**\n * A channel is the part of a `Client` of grpc-js 1.14 that the wrapper uses. A fake for tests has\n * each part too. Thus the package depends on no type of grpc-js.\n */\nexport interface GrpcChannel {\n /** This method gives the state of the channel. True also starts a connect when the channel is IDLE. */\n state(connect: boolean): GrpcState;\n /** This method starts `changed` one time, when the state of the channel is no longer `current`. */\n watch(current: GrpcState, changed: () => void): void;\n /**\n * This method sends one unary call: the method path and the bytes of its request message. `done`\n * gets the status of the call, and the bytes of the response message when the status is OK. The\n * call ends at `timeout` milliseconds with DEADLINE_EXCEEDED, and grpc-js sends the header `grpc-timeout`.\n */\n unary(\n method: string,\n request: Buffer,\n metadata: Readonly<Record<string, string>>,\n timeout: number,\n done: (status: GrpcStatus, response: Buffer | undefined) => void,\n ): GrpcCallHandle;\n /** This method closes the channel. Its state becomes SHUTDOWN, and it does not connect again. */\n close(): void;\n}\n\n/**\n * These are the options of the channel that the wrapper gives `new Client(target, credentials, options)`.\n * Each time comes from the declaration, in milliseconds.\n */\nexport interface GrpcChannelOptions {\n /** The wrapper turns off the retries of grpc-js, the transparent retry too. Only the core decides a second send. */\n readonly \"grpc.enable_retries\": 0;\n /** The wrapper reads no service config from the resolver. Thus no record of DNS changes the policy of a call. */\n readonly \"grpc.service_config_disable_resolution\": 1;\n /** This time is the interval of the HTTP/2 PING of keepalive. Without it, grpc-js sends no PING. */\n readonly \"grpc.keepalive_time_ms\"?: number | undefined;\n /** This time bounds the ACK of each PING. Without an ACK in this time, grpc-js ends the transport. */\n readonly \"grpc.keepalive_timeout_ms\": number;\n /** 1 sends the PING with no call in flight too. The server must permit that, or it answers with GOAWAY too_many_pings (O26). */\n readonly \"grpc.keepalive_permit_without_calls\": 0 | 1;\n /** This time with no call puts the channel in IDLE, and closes its connection. */\n readonly \"grpc.client_idle_timeout_ms\": number;\n /** This time is the first delay of grpc-js before a new try to connect. */\n readonly \"grpc.initial_reconnect_backoff_ms\": number;\n /** This time is the most that one delay of grpc-js can be, before its jitter of 20 %. */\n readonly \"grpc.max_reconnect_backoff_ms\": number;\n}\n\n/** These are the arguments of `new Client(target, credentials, options)`. The credentials are insecure: the client speaks h2c. */\nexport interface GrpcConnectOptions {\n readonly target: string;\n readonly options: GrpcChannelOptions;\n}\n\n/** A channel factory makes one channel from its options. The channel connects later, when the wrapper asks it. */\nexport type GrpcFactory = Factory<GrpcConnectOptions, GrpcChannel>;\n\n/** This type is the part of a `Client` of grpc-js 1.14 that the adapter uses. */\ninterface GrpcDriverClient {\n getChannel(): {\n getConnectivityState(connect: boolean): number;\n watchConnectivityState(current: number, deadline: number, callback: (error?: Error) => void): void;\n };\n makeUnaryRequest(\n method: string,\n serialize: (value: Buffer) => Buffer,\n deserialize: (bytes: Buffer) => Buffer,\n argument: Buffer,\n metadata: unknown,\n options: { readonly deadline: number },\n callback: (error: { readonly code?: number; readonly details?: string; readonly message?: string } | null, value?: Buffer) => void,\n ): GrpcCallHandle;\n close(): void;\n}\n\n/** This type is the part of the module of grpc-js that the adapter uses. */\ninterface GrpcDriver {\n readonly Client: new (target: string, credentials: unknown, options: GrpcChannelOptions) => GrpcDriverClient;\n readonly credentials: { createInsecure(): unknown };\n readonly Metadata: new () => { add(key: string, value: string): void };\n}\n\n/** These functions give the bytes of a message as they are. The caller encodes and decodes its messages. */\nconst identity = (bytes: Buffer): Buffer => bytes;\n\n/** This function gives the state of a value of the enum of grpc-js. */\nconst stateOf = (value: number): GrpcState => GRPC_STATES[value] ?? \"SHUTDOWN\";\n\n/**\n * This function adapts a `Client` of grpc-js to a channel. The deadline of grpc-js is a time of the\n * wall clock, thus the adapter adds the timeout to `Date.now()`.\n */\nexport const adaptGrpc = (grpc: GrpcDriver, client: GrpcDriverClient): GrpcChannel => {\n const channel = client.getChannel();\n return {\n state: (connect) => stateOf(channel.getConnectivityState(connect)),\n watch(current, changed) {\n try {\n channel.watchConnectivityState(GRPC_STATES.indexOf(current), Number.POSITIVE_INFINITY, (error) => {\n if (error === undefined) changed();\n });\n } catch {\n // The grpc-js driver throws when the channel is shut down: no state comes after SHUTDOWN\n }\n },\n unary(method, request, metadata, timeout, done) {\n const sent = new grpc.Metadata();\n for (const [key, value] of Object.entries(metadata)) sent.add(key, value);\n const deadline = Number.isFinite(timeout) ? Date.now() + timeout : Number.POSITIVE_INFINITY;\n return client.makeUnaryRequest(method, identity, identity, request, sent, { deadline }, (error, value) => {\n if (error === null) done({ code: 0, details: \"\" }, value ?? Buffer.alloc(0));\n else done({ code: error.code ?? 2, details: error.details ?? error.message ?? \"\" }, undefined);\n });\n },\n close: () => client.close(),\n };\n};\n\n/**\n * This factory makes a `Client` of grpc-js. It loads grpc-js only when a client connects, thus a\n * service that does not use gRPC does not need it. The specifier is a value, thus the package needs no types of grpc-js.\n */\nexport const nodeGrpc: GrpcFactory = async ({ target, options }) => {\n const specifier = \"@grpc/grpc-js\";\n const loaded = (await import(specifier)) as Partial<GrpcDriver> & { readonly default?: GrpcDriver };\n const grpc = loaded.Client === undefined ? loaded.default : (loaded as GrpcDriver);\n if (grpc?.Client === undefined) throw new Error(\"@grpc/grpc-js gives no Client class\");\n return adaptGrpc(grpc, new grpc.Client(target, grpc.credentials.createInsecure(), options));\n};\n","import type { Detector, Meeting, Situation } from \"../client/meets.ts\";\nimport { NEVER, WHAT, immediate, kernelGiveUp } from \"../client/meets.ts\";\nimport { finiteMs } from \"../client/core.ts\";\nimport { heartbeatDetection } from \"../timing.ts\";\nimport type { GrpcKnobs } from \"./client.ts\";\n\n/**\n * This function gives the manifestations that a gRPC client can meet, and the detectors of each,\n * from its knobs. The gRPC cells measured them on grpc-js 1.14.6:\n * - At its defaults, grpc-js has no detector: no keepalive, no deadline, and no TCP keepalive.\n * - A reset gave each call ECONNRESET in 1.1 ms to 1.4 ms. The FIN of a stopped server ended an idle connection in 43 µs.\n * - The keepalive ended a silent transport at keepalive_time + keepalive_timeout: 80.012 s at 60 s\n * against etcd, and 320.005 s at 300 s against grpc-go (DP24).\n * - Without `keepaliveWithoutCalls`, grpc-js sends a PING only with a call in flight.\n * - At client_idle_timeout, the channel goes IDLE, and the client connects again. A server that does\n * not answer then keeps the channel out of READY.\n */\nexport const grpcMeetings = (name: string, knobs: GrpcKnobs, withoutCalls: boolean): Meeting[] => {\n const interval = finiteMs(knobs.keepalive_time);\n const detection = heartbeatDetection(knobs.keepalive_time, knobs.keepalive_timeout, 1);\n const keepalive = (situation: Situation): Detector => ({\n by: \"heartbeat_detection_time\",\n within: situation === \"idle\" && !withoutCalls ? NEVER : detection,\n situation,\n effect:\n situation === \"idle\" && !withoutCalls\n ? \"grpc-js sends no PING with no call in flight\"\n : `the PING gets no ACK in keepalive_timeout, the transport ends, each call in flight fails with UNAVAILABLE, and ${name} connects again`,\n });\n const deadline: Detector = {\n by: \"request_timeout\",\n within: knobs.request_timeout,\n situation: \"in flight\",\n effect: \"the deadline ends the call with DEADLINE_EXCEEDED as indeterminate, and the transport stays\",\n };\n // A new connection after client_idle_timeout meets the failure, and the channel does not become READY\n const reconnect: Detector = {\n by: \"client_idle_timeout\",\n within: knobs.client_idle_timeout,\n situation: \"idle\",\n effect: `the channel goes IDLE, ${name} connects again, and the new connection does not become ready`,\n };\n const giveUp = kernelGiveUp(knobs.retrans_giveup_time, \"each call in flight fails with UNAVAILABLE\");\n const silent = (manifestation: \"blackhole\" | \"freeze-host\"): Meeting => ({\n manifestation,\n what: WHAT[manifestation],\n detectors: [deadline, keepalive(\"in flight\"), giveUp, keepalive(\"idle\"), reconnect],\n });\n return [\n {\n manifestation: \"rst\",\n what: WHAT.rst,\n detectors: [\n immediate(\"in flight\", \"the reset arrives, each call fails with UNAVAILABLE as indeterminate, and the channel goes IDLE\"),\n {\n by: \"keepalive_time\",\n within: interval === undefined || !withoutCalls ? NEVER : knobs.keepalive_time,\n situation: \"idle\",\n effect: \"the next PING gets a reset, and the client connects again\",\n },\n { ...reconnect, effect: `the channel goes IDLE, ${name} connects again, and the server refuses the connect` },\n ],\n },\n {\n manifestation: \"fin\",\n what: WHAT.fin,\n detectors: [\n immediate(\"in flight\", \"the FIN arrives, and each call fails with UNAVAILABLE as indeterminate\"),\n immediate(\"idle\", \"the FIN arrives, the channel goes IDLE, and the client connects again\"),\n ],\n },\n silent(\"blackhole\"),\n silent(\"freeze-host\"),\n {\n manifestation: \"freeze-process\",\n what: WHAT[\"freeze-process\"],\n // The kernel of the server completes a new TCP connect, but its process sends no SETTINGS frame\n detectors: [deadline, keepalive(\"in flight\"), keepalive(\"idle\"), reconnect],\n },\n {\n manifestation: \"app-stall\",\n what: \"the server holds its reply, for example in a slow handler, and its library still answers each PING\",\n detectors: [\n deadline,\n { ...keepalive(\"idle\"), within: NEVER, effect: \"the library of the server still answers each PING with an ACK\" },\n { ...reconnect, within: NEVER, effect: \"the library of the server still takes a new connection\" },\n ],\n },\n ];\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { realClock } from \"../clock.ts\";\nimport type {\n Attempt,\n CheckOptions,\n Client,\n ClientJoin,\n ClientKnobs,\n ClientOptions,\n ClientSpec,\n ClientStep,\n DialFn,\n RetryKnobs,\n SendOptions,\n Sent,\n} from \"../client/core.ts\";\nimport {\n ageProblems,\n attachClient,\n clientBounds,\n clientProblems,\n clientRecorder,\n dialCheck,\n finiteMs,\n idleCloseProblem,\n inFlight,\n localStateCheck,\n message,\n ms,\n notSent,\n operationTable,\n readyWait,\n retryProblems,\n roundTripCheck,\n sendWithReissue,\n socketEnds,\n stepChecks,\n timeProblems,\n wholeProblems,\n withinBound,\n wrapperTimerProblems,\n zeroProblems,\n} from \"../client/core.ts\";\nimport type { Judge } from \"../client/dial.ts\";\nimport { authority, helloDial } from \"../client/dial.ts\";\nimport { NEVER } from \"../client/meets.ts\";\nimport type { Dependency } from \"../description.ts\";\nimport type { DependencyHandle } from \"../lifecycle.ts\";\nimport type { Operation } from \"../operation.ts\";\nimport { heartbeatDetection } from \"../timing.ts\";\nimport type { GrpcCallHandle, GrpcChannel, GrpcConnectOptions, GrpcFactory, GrpcState, GrpcStatus } from \"./connection.ts\";\nimport { nodeGrpc } from \"./connection.ts\";\nimport { grpcMeetings } from \"./meets.ts\";\n\n/**\n * These are the knobs of the client, each a time, by the names of the ordering law and of the gRPC\n * cells. The runtime has no defaults, thus each knob is necessary. The constant\n * `GRPC_CLIENT_STARTING_KNOBS` gives a declaration from the gRPC cells. The knobs that each client has come from the core.\n */\nexport interface GrpcKnobs extends ClientKnobs, RetryKnobs {\n /** This time bounds the wait of the startup for a ready channel. The grpc-js driver has no connect timeout, and the wrapper cannot stop one connect. */\n readonly connect_bound: Quantity;\n /** This time is the deadline of each call, from its start. A second try ends in the same bound. The grpc-js driver sends it as `grpc-timeout`. */\n readonly request_timeout: Quantity;\n /** This time is the interval of the HTTP/2 PING of keepalive (`grpc.keepalive_time_ms`). \"infinite\" sends no PING, as grpc-js does at its defaults. */\n readonly keepalive_time: Extent;\n /** This time bounds the ACK of each PING (`grpc.keepalive_timeout_ms`). With no ACK, grpc-js ends the transport. */\n readonly keepalive_timeout: Quantity;\n /** This time with no call puts the channel in IDLE and closes its connection (`grpc.client_idle_timeout_ms`). The client then connects again. */\n readonly client_idle_timeout: Quantity;\n /**\n * This time is the shortest interval of the PING that the server permits: `MinTime` of the\n * enforcement policy of grpc-go. A shorter keepalive gets GOAWAY too_many_pings (O26). It is a fact of the server, not a knob of the client.\n */\n readonly server_keepalive_min_time: Quantity;\n /** This time is when the server or a proxy ends an idle connection with GOAWAY, or \"infinite\". The client must close first (O01). */\n readonly server_idle_close: Extent;\n /** This time is the first delay of grpc-js before a new try to connect (`grpc.initial_reconnect_backoff_ms`). */\n readonly retry_base: Quantity;\n /** This time is the most that one delay of grpc-js can be, before its jitter of 20 % (`grpc.max_reconnect_backoff_ms`). */\n readonly retry_cap: Quantity;\n /** The grpc-js driver sets no TCP keepalive on its socket: the PING of `keepalive_time` is its keepalive. Thus this knob must be \"infinite\". */\n readonly keepalive_idle: Extent;\n /** The client does not replace its connection at an age, thus this knob must be \"infinite\". */\n readonly max_connection_age: Extent;\n /** This time bounds the close at the shutdown: the calls in flight. At the bound, the client cancels each call, then closes the channel. */\n readonly close_bound: Quantity;\n}\n\n/** A gRPC client spec is the declaration of one gRPC client: where it connects, and its knobs. */\nexport interface GrpcClientSpec extends ClientSpec<GrpcKnobs> {\n /**\n * True sends the PING of keepalive with no call in flight too (`grpc.keepalive_permit_without_calls`).\n * The server must permit that (`PermitWithoutStream` of grpc-go), or it answers with GOAWAY too_many_pings (O26).\n */\n readonly keepaliveWithoutCalls: boolean;\n /** These headers go with each call, for example `authorization`. The metadata of a call adds to them. */\n readonly metadata?: Readonly<Record<string, string>> | undefined;\n /** This name is the service that the round-trip check asks the health service about. The default is \"\", the whole server. */\n readonly healthService?: string | undefined;\n /**\n * These operations are the methods of the service, by their full path, with their idempotency. The\n * client sends again only a method that this table names as idempotent. `grpcOperations` makes the\n * table from the `idempotency_level` of each method in its proto.\n */\n readonly operations?: ClientSpec<GrpcKnobs>[\"operations\"];\n}\n\n/** These options make a client. Without them, it uses grpc-js, the real clock, and a dial with the HTTP/2 preface. */\nexport type GrpcClientOptions = ClientOptions<GrpcFactory>;\n\n/**\n * A join is one generation of the client: each time that its channel becomes READY (D1, D2). The\n * grpc-js driver gives no socket of its transport, thus the ends of the socket are null.\n */\nexport interface GrpcJoin extends ClientJoin {\n /** This text is the target of the channel. */\n readonly target: string;\n}\n\n/** These options tell how the client sends one call. */\nexport interface GrpcCallOptions extends SendOptions {\n /** These headers go with this call, after the metadata of the spec. */\n readonly metadata?: Readonly<Record<string, string>> | undefined;\n}\n\n/** A serving status is the status of a HealthCheckResponse of the health service (grpc.health.v1). */\nexport type GrpcServingStatus = \"UNKNOWN\" | \"SERVING\" | \"NOT_SERVING\" | \"SERVICE_UNKNOWN\";\n\n/** A gRPC client is a dependency of a service: its declaration, its handle, its checks and its calls. */\nexport interface GrpcClient extends Client<ClientStep, GrpcJoin> {\n /** This text is the state of the client, as `GRPC_CLIENT_MACHINE` names it. */\n readonly status: string;\n /**\n * This method sends one unary call, and waits for its status. `request` holds the bytes of the\n * request message, and an OK status gives the bytes of the response message. The client sends again\n * only an idempotent method, one time at most, after an outcome that is not certain.\n */\n call(method: string, request: Buffer, options?: GrpcCallOptions): Promise<Sent<Buffer>>;\n /** This method sends one Check of the health service (grpc.health.v1), and gives the serving status of the reply. */\n health(service?: string, options?: SendOptions): Promise<Sent<GrpcServingStatus>>;\n}\n\n/** The names of the status codes of gRPC, by their value. */\nexport const GRPC_CODES = [\n \"OK\",\n \"CANCELLED\",\n \"UNKNOWN\",\n \"INVALID_ARGUMENT\",\n \"DEADLINE_EXCEEDED\",\n \"NOT_FOUND\",\n \"ALREADY_EXISTS\",\n \"PERMISSION_DENIED\",\n \"RESOURCE_EXHAUSTED\",\n \"FAILED_PRECONDITION\",\n \"ABORTED\",\n \"OUT_OF_RANGE\",\n \"UNIMPLEMENTED\",\n \"INTERNAL\",\n \"UNAVAILABLE\",\n \"DATA_LOSS\",\n \"UNAUTHENTICATED\",\n] as const;\n\n/**\n * These codes are certain errors: each one tells that the server or a proxy refused the call, and did\n * not apply it. A proxy that answers 401, 403 or 404 gives UNAUTHENTICATED, PERMISSION_DENIED or UNIMPLEMENTED.\n */\nconst CERTAIN = new Set([3, 5, 6, 7, 9, 10, 11, 12, 16]);\n\n/**\n * These statuses of grpc-js tell that the call did not leave the client. A status with another\n * details text of the same code can come after the send, thus it is indeterminate:\n * - UNAVAILABLE \"No connection established\": the pick of a channel in TRANSIENT_FAILURE (picker.ts:105).\n * - UNAVAILABLE \"Name resolution failed\": the resolver found no address (resolver-dns.ts:119).\n * - UNAVAILABLE \"Stream refused by server\": RST_STREAM REFUSED_STREAM. The server did not process the stream (RFC 9113 §8.7).\n * - INTERNAL \"Failed to start HTTP/2 stream\": the stream did not start (load-balancing-call.ts:281).\n * - DEADLINE_EXCEEDED with \"Waiting for LB pick\" or \"waiting for name resolution\": the deadline came before a stream.\n */\nconst NOT_SENT: ReadonlyArray<readonly [number, RegExp]> = [\n [14, /^No connection established/],\n [14, /^Name resolution failed/],\n [14, /^Stream refused by server$/],\n [13, /^Failed to start HTTP\\/2 stream/],\n [4, /(^|,)(Waiting for LB pick|waiting for name resolution)(,|$)/],\n];\n\n/** This function gives the name of a status code, for example \"UNAVAILABLE\". */\nexport const codeName = (code: number): string => GRPC_CODES[code] ?? `code ${code}`;\n\n/**\n * This function gives the outcome of one call from its status:\n * - OK is `ok`.\n * - A code that tells that the server refused the call is a certain error: `reply` is true.\n * - A status that tells that the call did not leave the client is a certain error with `reply` false.\n * - Each other status is indeterminate: CANCELLED, UNKNOWN, DEADLINE_EXCEEDED, RESOURCE_EXHAUSTED,\n * INTERNAL, UNAVAILABLE and DATA_LOSS can come after the server applied the call.\n */\nexport const grpcOutcome = (status: GrpcStatus, response: Buffer | undefined): Attempt<Buffer> => {\n if (status.code === 0) return { outcome: \"ok\", value: response ?? Buffer.alloc(0) };\n const error = `${codeName(status.code)}: ${status.details}`;\n if (CERTAIN.has(status.code)) return { outcome: \"error\", error, reply: true };\n if (NOT_SENT.some(([code, details]) => code === status.code && details.test(status.details))) return notSent(`${error} (the call did not leave the client)`);\n return { outcome: \"indeterminate\", error };\n};\n\n/** This method is the Check of the health service. Its request names a service, and its response gives a serving status. */\nexport const HEALTH_CHECK = \"/grpc.health.v1.Health/Check\";\n\n/** These operations are in each table: the Check of the health service has no side effects. */\nexport const GRPC_OPERATIONS: Readonly<Record<string, Operation>> = { [HEALTH_CHECK]: { idempotent: true } };\n\n/** An idempotency level is the option `idempotency_level` of a method in its proto (descriptor.proto). */\nexport type GrpcIdempotencyLevel = \"IDEMPOTENCY_UNKNOWN\" | \"NO_SIDE_EFFECTS\" | \"IDEMPOTENT\";\n\n/**\n * This function makes a table of operations from the idempotency level of each method, by its full\n * path. NO_SIDE_EFFECTS and IDEMPOTENT are idempotent. IDEMPOTENCY_UNKNOWN, the default of a proto, is not.\n */\nexport const grpcOperations = (levels: Readonly<Record<string, GrpcIdempotencyLevel>>): Record<string, Operation> =>\n Object.fromEntries(Object.entries(levels).map(([method, level]) => [method, { idempotent: level !== \"IDEMPOTENCY_UNKNOWN\" }]));\n\n/** This function gives the bytes of a varint of protobuf. */\nconst varint = (value: number): Buffer => {\n const bytes: number[] = [];\n let rest = value;\n while (rest > 0x7f) {\n bytes.push((rest & 0x7f) | 0x80);\n rest = Math.floor(rest / 128);\n }\n bytes.push(rest);\n return Buffer.from(bytes);\n};\n\n/** This function gives the bytes of a HealthCheckRequest. The service \"\" is the whole server, and its message is empty. */\nexport const healthRequest = (service: string): Buffer => {\n if (service === \"\") return Buffer.alloc(0);\n const name = Buffer.from(service, \"utf8\");\n return Buffer.concat([Buffer.from([0x0a]), varint(name.length), name]);\n};\n\nconst SERVING: readonly GrpcServingStatus[] = [\"UNKNOWN\", \"SERVING\", \"NOT_SERVING\", \"SERVICE_UNKNOWN\"];\n\n/** This function gives the status of a HealthCheckResponse, or null when the bytes are not one. An empty message is UNKNOWN. */\nexport const healthStatus = (bytes: Buffer): GrpcServingStatus | null => {\n let at = 0;\n let status = 0;\n const read = (): number | null => {\n let value = 0;\n for (let shift = 0; shift < 35; shift += 7) {\n const byte = bytes[at];\n if (byte === undefined) return null;\n at += 1;\n value += (byte & 0x7f) * 2 ** shift;\n if ((byte & 0x80) === 0) return value;\n }\n return null;\n };\n while (at < bytes.length) {\n const tag = read();\n if (tag === null) return null;\n const wire = tag & 7;\n if (wire === 0) {\n const value = read();\n if (value === null) return null;\n if (tag >> 3 === 1) status = value;\n } else if (wire === 2) {\n const length = read();\n if (length === null || at + length > bytes.length) return null;\n at += length;\n } else if (wire === 1 || wire === 5) {\n at += wire === 1 ? 8 : 4;\n if (at > bytes.length) return null;\n } else return null;\n }\n return SERVING[status] ?? null;\n};\n\n/** The longest timer of Node, in milliseconds. A longer delay starts at 1 ms, thus grpc-js cannot take it. */\nconst LONGEST_TIMER = 0x7fffffff;\n\n/** This function gives the problems of the times that grpc-js gives to `setTimeout` with no check: each one is at most the longest timer. */\nconst longestTimerProblems = (name: string, knobs: object, names: readonly string[]): string[] =>\n names.flatMap((knob) => {\n const amount = (knobs as Record<string, Extent>)[knob]!;\n return amount.value !== \"infinite\" && ms(amount) > LONGEST_TIMER ? [`${name}'s ${knob} is above ${LONGEST_TIMER} ms, the longest timer of Node`] : [];\n });\n\n/** This function gives the problems of a spec: the knobs that the client cannot use. */\nexport const specProblems = (name: string, spec: GrpcClientSpec): string[] => {\n const { knobs } = spec;\n const timers = [\"keepalive_time\", \"keepalive_timeout\", \"client_idle_timeout\", \"retry_base\", \"retry_cap\"];\n const problems = [\n ...timeProblems(name, knobs),\n // The grpc-js driver has no connect timeout, and gives a call no deadline: the wrapper gives both\n ...wrapperTimerProblems(name, knobs, { connect_bound: \"the wait of the startup for a ready channel\", request_timeout: \"a call\" }),\n ...zeroProblems(name, \"grpc-js\", knobs, { open: [\"keepalive_time\"] }),\n // The grpc-js driver takes each timer in whole milliseconds\n ...wholeProblems(name, knobs, { millis: timers }),\n ...longestTimerProblems(name, knobs, timers),\n ...retryProblems(name, knobs),\n ...ageProblems(name, knobs, \"grpc-js does not replace its connection at an age\"),\n ];\n for (const knob of [\"keepalive_timeout\", \"client_idle_timeout\", \"retry_base\", \"retry_cap\"] as const)\n if (finiteMs(knobs[knob]) === undefined || !(ms(knobs[knob]) > 0)) problems.push(`${name}'s ${knob} is not a finite time above zero`);\n // The grpc-js driver raises an idle timeout below 1 s to 1 s (internal-channel.ts:323-326)\n if (ms(knobs.client_idle_timeout) < 1000) problems.push(`${name}'s client_idle_timeout is below 1 s: grpc-js raises it to 1 s`);\n if (knobs.keepalive_idle.value !== \"infinite\")\n problems.push(`${name}'s keepalive_idle is not \"infinite\": grpc-js sets no TCP keepalive, and the PING of keepalive_time is its keepalive`);\n if (spec.password !== undefined) problems.push(`${name} takes no password: give the credentials in metadata, for example authorization`);\n return problems;\n};\n\n/** This function gives the target of a channel from a spec: the host and the port, for the DNS resolver of grpc-js. */\nexport const grpcTarget = (spec: Pick<GrpcClientSpec, \"host\" | \"port\">): string => authority(spec.host, spec.port);\n\n/**\n * This function gives the arguments of `new Client` from a spec. Each timer of grpc-js comes from the\n * declaration. The retries of grpc-js are off, and the resolver gives no service config.\n */\nexport const grpcOptions = (spec: GrpcClientSpec): GrpcConnectOptions => {\n const { knobs } = spec;\n const keepalive = finiteMs(knobs.keepalive_time);\n return {\n target: grpcTarget(spec),\n options: {\n \"grpc.enable_retries\": 0,\n \"grpc.service_config_disable_resolution\": 1,\n ...(keepalive === undefined ? {} : { \"grpc.keepalive_time_ms\": keepalive }),\n \"grpc.keepalive_timeout_ms\": ms(knobs.keepalive_timeout),\n \"grpc.keepalive_permit_without_calls\": spec.keepaliveWithoutCalls ? 1 : 0,\n \"grpc.client_idle_timeout_ms\": ms(knobs.client_idle_timeout),\n \"grpc.initial_reconnect_backoff_ms\": ms(knobs.retry_base),\n \"grpc.max_reconnect_backoff_ms\": ms(knobs.retry_cap),\n },\n };\n};\n\n/** These are the bytes of the dial: the connection preface of the client (RFC 9113 §3.4), and a SETTINGS frame with no setting. */\nexport const HTTP2_PREFACE: Buffer = Buffer.concat([Buffer.from(\"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\", \"latin1\"), Buffer.from([0, 0, 0, 4, 0, 0, 0, 0, 0])]);\n\n/**\n * This judge reads the first frame of the server. Only a SETTINGS frame on stream 0, with no ACK,\n * passes: RFC 9113 §3.4 makes it the first frame of an HTTP/2 server. An HTTP/1.1 server answers with a status line.\n */\nexport const settingsJudge: Judge = (received) => {\n if (received.length < 9) return \"more\";\n const type = received[3];\n const flags = received[4]!;\n const stream = received.readUInt32BE(5) & 0x7fffffff;\n return type === 0x04 && (flags & 0x01) === 0 && stream === 0\n ? null\n : new Error(`the server did not answer the HTTP/2 preface with a SETTINGS frame: its first bytes are ${received.subarray(0, 9).toString(\"hex\")}`);\n};\n\n/** This dial opens a new TCP connection, sends the HTTP/2 preface, and waits for the SETTINGS frame of the server. The dial then destroys its socket. */\nexport const grpcDial: DialFn = helloDial(HTTP2_PREFACE, settingsJudge);\n\n/**\n * This function makes a gRPC client, a dependency of a service over grpc-js 1.14.\n * - It has one channel for its life. Each time that the channel becomes READY is one generation.\n * - The grpc-js driver connects again by itself, after its own backoff. The client connects an IDLE channel again at once.\n * - A call is one unary call, until its status. Its deadline is the time that is left in `request_timeout`.\n * - It tells the outcome of a call in three values from its status code (C8).\n * - The retries of grpc-js are off, thus only the core sends a call again.\n * - Its keepalive is the HTTP/2 PING of grpc-js. It records the target of each generation (D1).\n *\n * These parts come from the core of the clients: the outcomes, the second try and the wait for a ready client.\n * The calls in flight, the bounded close and the checks come from the core too. This entry gives what grpc-js does in its own way.\n */\nexport const grpcClient = (name: string, spec: GrpcClientSpec, options: GrpcClientOptions = {}): GrpcClient => {\n const problems = specProblems(name, spec);\n if (problems.length > 0) throw new Error(problems.join(\"; \"));\n const { knobs } = spec;\n const clock = options.clock ?? realClock;\n const create = options.create ?? nodeGrpc;\n const dial = options.dial ?? grpcDial;\n const operations = operationTable(GRPC_OPERATIONS, spec.operations);\n const requestTimeout = ms(knobs.request_timeout);\n const target = grpcTarget(spec);\n\n let status = \"none\";\n let channel: GrpcChannel | null = null;\n // This function ends the wait of the startup, at the first READY or TRANSIENT_FAILURE\n let settled: (() => void) | null = null;\n const joins: GrpcJoin[] = [];\n const flight = inFlight();\n const readied = readyWait(clock, () => status === \"ready\");\n\n const emit = clientRecorder(options.record, clock, name);\n const record = (on: string, to: string = status, detail?: string): void => {\n const from = status;\n status = to;\n emit(from, to, on, detail);\n };\n const closing = (): boolean => status === \"closing\" || status === \"end\";\n\n /** This function follows one state of the channel. The grpc-js driver ends each call in flight by itself when its transport ends. */\n const follow = (c: GrpcChannel, state: GrpcState): void => {\n if (c !== channel || closing()) return;\n if (state === \"READY\") {\n if (status === \"ready\") return;\n const generation = joins.length + 1;\n joins.push({ generation, at: clock.now(), target, ...socketEnds(undefined) });\n record(\"join\", \"ready\", `generation ${generation}, ${target}`);\n readied.wake();\n settled?.();\n } else if (state === \"TRANSIENT_FAILURE\") {\n if (status === \"failing\") return;\n record(\"connect.failed\", \"failing\", \"the channel is in TRANSIENT_FAILURE: grpc-js connects again after its backoff\");\n settled?.();\n } else if (state === \"CONNECTING\") {\n if (status !== \"connecting\") record(\"channel.connecting\", \"connecting\", target);\n } else if (state === \"IDLE\") {\n if (status === \"ready\") record(\"connection.end\", \"idle\", \"the transport ended: a GOAWAY, a reset, a FIN, the keepalive, or client_idle_timeout\");\n else if (status !== \"idle\") record(\"channel.idle\", \"idle\", \"the channel is IDLE\");\n // An IDLE channel connects only for a call: the client connects at once, thus readiness is earned again\n c.state(true);\n }\n };\n\n /** This function watches the state of the channel. It watches again before it follows, thus no change goes away. */\n const watch = (c: GrpcChannel, current: GrpcState): void =>\n c.watch(current, () => {\n if (c !== channel || closing()) return;\n const next = c.state(false);\n if (next !== \"SHUTDOWN\") watch(c, next);\n follow(c, next);\n });\n\n /** This function gives the channel for a call, or the reason that the call does not leave the client. */\n const usable = (signal: AbortSignal | undefined): GrpcChannel | Attempt<never> => {\n if (status === \"none\") return notSent(`${name} has not connected`);\n if (status !== \"ready\" || channel === null) return notSent(`${name} is ${status}, not ready: the call did not leave the client`);\n if (signal?.aborted) return notSent(\"the caller aborted the call before it left the client\");\n return channel;\n };\n\n /** This function makes one try of a call: the unary call until its status, with the deadline that is left. */\n const attempt = (\n method: string,\n request: Buffer,\n metadata: Readonly<Record<string, string>>,\n timeout: number,\n signal: AbortSignal | undefined,\n ): Promise<Attempt<Buffer>> => {\n const c = usable(signal);\n if (\"outcome\" in c) return Promise.resolve(c);\n if (!(timeout > 0)) return Promise.resolve(notSent(\"no time is left in request_timeout\"));\n let handle: GrpcCallHandle | null = null;\n let ended = false;\n return flight.track<Buffer>(\n signal,\n \"the caller abandoned the call after it left the client\",\n (settle) => {\n try {\n handle = c.unary(method, request, metadata, timeout, (given, response) => {\n ended = true;\n settle(grpcOutcome(given, response));\n });\n } catch (error) {\n ended = true;\n settle(notSent(message(error)));\n }\n },\n // A call that the caller abandons, or that the close ends, gets RST_STREAM CANCEL\n () => {\n if (!ended) handle?.cancel();\n },\n );\n };\n\n const call = (method: string, request: Buffer, callOptions: GrpcCallOptions = {}): Promise<Sent<Buffer>> => {\n const started = clock.now();\n const metadata = { ...spec.metadata, ...callOptions.metadata };\n return sendWithReissue({\n clock,\n bound: requestTimeout,\n operation: operations[method],\n options: callOptions,\n // The deadline of each try is the time that is left: a second try ends at the bound of the call\n attempt: (signal) => attempt(method, request, metadata, requestTimeout - (clock.now() - started), signal),\n // The grpc-js driver connects again by itself: the second try waits for a ready channel, in the time that is left\n beforeAgain: (left, signal) => readied.wait(left, signal),\n onReissue: () => record(\"command.reissue\", status, method),\n });\n };\n\n const health = async (service = \"\", sendOptions: SendOptions = {}): Promise<Sent<GrpcServingStatus>> => {\n const sent = await call(HEALTH_CHECK, healthRequest(service), sendOptions);\n if (sent.outcome !== \"ok\") return sent;\n const serving = healthStatus(sent.value);\n return serving === null\n ? { outcome: \"error\", error: \"the response is not a HealthCheckResponse\", reply: true, attempts: sent.attempts }\n : { outcome: \"ok\", value: serving, attempts: sent.attempts };\n };\n\n const handle: DependencyHandle = {\n async connect(signal) {\n if (signal.aborted) throw new Error(`${name}: the service stopped before the connect`);\n record(\"status.connecting\", \"connecting\", target);\n let c: GrpcChannel;\n try {\n c = await create(grpcOptions(spec));\n } catch (error) {\n record(\"connect.failed\", \"failing\", message(error));\n throw error;\n }\n if (closing()) {\n c.close();\n return;\n }\n channel = c;\n const first = new Promise<void>((resolve) => (settled = resolve));\n // A server that is down does not stop the startup: grpc-js connects again in the background,\n // and the checks keep readiness tripped until the channel is ready (readiness is earned)\n const now = c.state(true);\n watch(c, now);\n if (now !== \"IDLE\") follow(c, now);\n // The wait ends at the first READY or TRANSIENT_FAILURE, or at connect_bound. A connect into a black hole stays CONNECTING\n await withinBound(clock, ms(knobs.connect_bound), first);\n settled = null;\n },\n async close() {\n if (status === \"none\" || closing()) return;\n record(\"close.request\", \"closing\");\n settled?.();\n const c = channel;\n if (c !== null) {\n // The calls in flight end first. At the bound, each one gets RST_STREAM CANCEL, and then the channel closes\n if (!(await withinBound(clock, ms(knobs.close_bound), flight.idle()))) {\n record(\"close.bound\", \"closing\");\n record(\"close.destroy\", \"closing\", `${flight.size} calls in flight`);\n flight.settleAll(\"the client closed with the call in flight\");\n }\n c.close();\n }\n channel = null;\n flight.settleAll(\"the client closed with the call in flight\");\n record(\"status.end\", \"end\");\n },\n };\n\n const keepalive = finiteMs(knobs.keepalive_time);\n const declaration: Omit<Dependency, \"name\"> = {\n system: spec.system,\n knobs: {\n connect_bound: knobs.connect_bound,\n request_timeout: knobs.request_timeout,\n keepalive_time: knobs.keepalive_time,\n keepalive_timeout: knobs.keepalive_timeout,\n // One PING at a time, and the transport ends at its timeout: keepalive_time + keepalive_timeout\n heartbeat_detection_time: heartbeatDetection(knobs.keepalive_time, knobs.keepalive_timeout, 1),\n client_idle_timeout: knobs.client_idle_timeout,\n retry_base: knobs.retry_base,\n retry_cap: knobs.retry_cap,\n keepalive_idle: knobs.keepalive_idle,\n // A PING with no call in flight refreshes an idle flow (O03). Without it, nothing does\n idle_refresh_interval: spec.keepaliveWithoutCalls && keepalive !== undefined ? knobs.keepalive_time : NEVER,\n max_connection_age: knobs.max_connection_age,\n close_bound: knobs.close_bound,\n retrans_giveup_time: knobs.retrans_giveup_time,\n },\n operations,\n };\n\n /** The round-trip check passes only for SERVING: a health service that answers NOT_SERVING fails it, as a 5xx fails the HTTP check. */\n const roundTrip = async (sendOptions: SendOptions): Promise<Sent<unknown>> => {\n const sent = await health(spec.healthService ?? \"\", sendOptions);\n return sent.outcome === \"ok\" && sent.value !== \"SERVING\"\n ? { outcome: \"error\", error: `the health service answered ${sent.value}`, reply: true, attempts: sent.attempts }\n : sent;\n };\n\n const checks = (checkOptions: CheckOptions) =>\n stepChecks<ClientStep>(name, checkOptions, {\n // The state of the channel, without a round trip\n \"local-state\": localStateCheck(() => status === \"ready\"),\n // A Check of the health service on the channel of the application, until its status\n \"round-trip\": roundTripCheck(roundTrip),\n // A new connection each time, with the HTTP/2 preface\n dial: dialCheck(dial, spec.host, spec.port),\n });\n\n return {\n name,\n declaration,\n handle,\n meets: grpcMeetings(name, knobs, spec.keepaliveWithoutCalls),\n get status() {\n return status;\n },\n joins,\n call,\n health,\n checks,\n attach: (svc, checkOptions) => attachClient(svc, name, declaration, handle, checks(checkOptions)),\n problems(description) {\n const found = clientProblems(name, description, clientBounds(knobs));\n // O01: the channel must close an idle connection before the server or a proxy does\n const idle = idleCloseProblem(name, knobs.client_idle_timeout, knobs.server_idle_close, \"client_idle_timeout\");\n if (idle !== null) found.push(idle);\n // O26: a PING more often than the server permits gets GOAWAY too_many_pings\n if (keepalive !== undefined && keepalive < ms(knobs.server_keepalive_min_time))\n found.push(`${name}'s keepalive_time is below server_keepalive_min_time: the server answers with GOAWAY too_many_pings (O26)`);\n return found;\n },\n };\n};\n","import type { Machine } from \"@mark1russell7/algebra\";\nimport { clientKnob as knob, closeTimer, transition as t } from \"../client/machine.ts\";\n\n/** The states where the client can close, and the states where a call can start its second try. */\nconst SERVING = [\"connecting\", \"ready\", \"idle\", \"failing\"] as const;\nconst LIVE = [...SERVING, \"closing\"] as const;\n\n/**\n * The gRPC client of the service as a `mark1russell7.machine/1` document. It is one channel of\n * grpc-js 1.14 for the life of the client. Each READY of the channel is one generation. The states:\n * - `ready`: the channel is READY, and the client sends each call. The keepalive sends a PING each keepalive_time.\n * - `idle`: the transport ended, or the channel had no call in client_idle_timeout. The client connects again at once.\n * - `failing`: the channel is in TRANSIENT_FAILURE. The grpc-js driver connects again after its backoff.\n * - `closing`: the calls in flight, in `close_bound`. At the bound, each call gets RST_STREAM CANCEL.\n *\n * A test records the transitions of the client and replays them against this document.\n */\nexport const GRPC_CLIENT_MACHINE: Machine = {\n format: \"mark1russell7.machine/1\",\n id: \"service.machine.grpc-client\",\n title: \"The gRPC client of the service, over grpc-js 1.14\",\n layer: \"driver\",\n version: \"0.1.0\",\n sources: [{ ref: \"packages/service/src/grpc (the client implements this document)\", rung: \"implemented\" }],\n states: {\n none: { initial: true, note: \"the client exists, and its channel does not\" },\n connecting: { note: \"the channel is CONNECTING: the TCP connect and the HTTP/2 preface. The grpc-js driver has no connect timeout\" },\n ready: { note: \"the channel is READY. The deadline ends a call that waits, and the keepalive finds a silent transport\" },\n idle: { note: \"the channel is IDLE. The client asks it to connect at once\" },\n failing: { note: \"the channel is in TRANSIENT_FAILURE. The grpc-js driver connects again after its backoff, and each call fails as not sent\" },\n closing: { note: \"the calls in flight, in close_bound. At the bound, each call gets RST_STREAM CANCEL, then the channel closes\" },\n end: { final: true, note: \"the service closed the client\" },\n },\n events: {\n \"status.connecting\": { kind: \"local\" },\n join: { kind: \"rx\", note: \"the channel became READY: a new generation (D1)\" },\n \"connect.failed\": { kind: \"rx\", note: \"the channel went to TRANSIENT_FAILURE: a connect failed\" },\n \"channel.connecting\": { kind: \"rx\", note: \"the channel started a new connect\" },\n \"channel.idle\": { kind: \"rx\", note: \"the channel went to IDLE before it was READY\" },\n \"connection.end\": {\n kind: \"rx\",\n note: \"the transport ended: a GOAWAY, a reset, a FIN, the keepalive, or client_idle_timeout. The grpc-js driver ends each call in flight with UNAVAILABLE\",\n },\n \"command.reissue\": { kind: \"local\" },\n \"close.request\": { kind: \"local\" },\n \"close.bound\": { kind: \"timer\", note: \"the calls in flight did not end in close_bound\" },\n \"close.destroy\": { kind: \"local\", note: \"the client cancels each call in flight, then closes the channel\" },\n \"status.end\": { kind: \"local\" },\n },\n knobs: {\n keepalive_time: knob(\"grpc.keepalive_time_ms\"),\n keepalive_timeout: knob(\"grpc.keepalive_timeout_ms\"),\n client_idle_timeout: knob(\"grpc.client_idle_timeout_ms\"),\n retry_base: knob(\"grpc.initial_reconnect_backoff_ms\"),\n retry_cap: knob(\"grpc.max_reconnect_backoff_ms\"),\n close_bound: knob(\"close.bound\"),\n },\n timers: {\n keepalive: {\n duration: { expr: \"keepalive_time + keepalive_timeout\", unit: \"s\" },\n arms: [\"join\"],\n cancels: [\"connection.end\", \"close.request\"],\n fires: \"connection.end\",\n note: \"a timer of grpc-js: one PING each keepalive_time, and the end of the transport at keepalive_timeout with no ACK\",\n },\n idle: {\n duration: { knob: \"client_idle_timeout\", unit: \"s\" },\n arms: [\"join\"],\n cancels: [\"connection.end\", \"close.request\"],\n fires: \"connection.end\",\n note: \"a timer of grpc-js: client_idle_timeout with no call puts the channel in IDLE\",\n },\n retry: {\n duration: { expr: \"min(retry_cap, retry_base * pow(1.6, fired)) * 1.2\", unit: \"s\" },\n arms: [\"connect.failed\"],\n cancels: [\"close.request\"],\n fires: \"join\",\n backoff: { resets: [\"join\"] },\n note: \"a timer of grpc-js, the most delay: the backoff grows by 1.6 from retry_base to retry_cap, with a jitter of 20 %. The first delay has no jitter\",\n },\n close: closeTimer(),\n },\n transitions: [\n t(\"connect\", \"none\", \"status.connecting\", \"connecting\"),\n t(\"join\", [\"connecting\", \"idle\", \"failing\"], \"join\", \"ready\"),\n t(\"connect-failed\", [\"connecting\", \"idle\", \"ready\"], \"connect.failed\", \"failing\"),\n t(\"retry\", \"failing\", \"join\", \"ready\", { timer: \"retry\" }),\n t(\"channel-connecting\", [\"idle\", \"failing\"], \"channel.connecting\", \"connecting\"),\n t(\"channel-idle\", [\"connecting\", \"failing\"], \"channel.idle\", \"idle\"),\n t(\"connection-end\", \"ready\", \"connection.end\", \"idle\"),\n t(\"keepalive\", \"ready\", \"connection.end\", \"idle\", { timer: \"keepalive\" }),\n t(\"idle-timeout\", \"ready\", \"connection.end\", \"idle\", { timer: \"idle\" }),\n ...LIVE.map((state) => t(`reissue-${state}`, state, \"command.reissue\", state)),\n t(\"close\", [...SERVING], \"close.request\", \"closing\"),\n t(\"close-bound\", \"closing\", \"close.bound\", \"closing\", { timer: \"close\" }),\n t(\"close-destroy\", \"closing\", \"close.destroy\", \"closing\"),\n t(\"end\", \"closing\", \"status.end\", \"end\"),\n ],\n};\n","import { STARTING_POLL } from \"../client/core.ts\";\nimport type { Poll } from \"../timing.ts\";\nimport type { GrpcKnobs } from \"./client.ts\";\n\n/**\n * This poll is the poll of the checks that `GRPC_CLIENT_STARTING_KNOBS` fits. It is `STARTING_POLL` of\n * the core: each 5 s with a jitter of 20 %, and a timeout of 4 s (B6, O12). The poll does not depend on the driver.\n */\nexport const GRPC_CLIENT_STARTING_POLL: Poll = STARTING_POLL;\n\n/**\n * This value tells if the keepalive sends a PING with no call in flight, to start from: false. The\n * grpc-go server refuses such a PING at its defaults (`PermitWithoutStream` false). It answers with GOAWAY too_many_pings (O26).\n */\nexport const GRPC_CLIENT_STARTING_WITHOUT_CALLS = false;\n\n/**\n * These knobs are a declaration to start from, from what the gRPC cells measured on grpc-js 1.14.6.\n * The runtime has no defaults: a service gives these knobs, or its own. Each value holds the borders that the cells measured:\n * - `connect_bound` 5 s: the connect timeout of DP44. The grpc-js driver has no connect timeout, thus this bounds the wait of the startup.\n * - `request_timeout` 9.5 s: in the slowest poll of 10 s (O13). In DP44 a deadline of 4 s ended each\n * call at 4.003 s to 4.004 s. The race of the two deadlines gave CANCELLED to 2 to 4 calls of 11.\n * - `keepalive_time` 300 s and `keepalive_timeout` 20 s: DP24. The PING must not come before the\n * `MinTime` of the server, 5 min at the defaults of grpc-go (O26). The transport ended at 320.005 s.\n * Against etcd, with a `MinTime` of 5 s, 60 s ended it at 80.012 s. A PING each 4 s against 5 s got GOAWAY at the 4th PING.\n * - `server_keepalive_min_time` 300 s: the `MinTime` of grpc-go v1.84.0 at its defaults.\n * - `client_idle_timeout` 60 s and `server_idle_close` 75 s: the proxies of the cells sent GOAWAY to an idle connection.\n * They did that at 75 s (nginx), 180 s (Traefik), 300 s (Caddy) and 1 h (Envoy). The client closes first (O01).\n * The default of grpc-js is 30 min.\n * - `retry_base` 1 s: the first backoff of grpc-js in T07, with no jitter. `retry_cap` 4 s: the most\n * delay is 4.8 s with the jitter of 20 %, in the slowest poll of 10 s. The default of grpc-js is 120 s.\n * - `keepalive_idle` \"infinite\": grpc-js sets no TCP keepalive, and the PING is its keepalive.\n * - `max_connection_age` \"infinite\": the client does not replace its connection at an age.\n * - `close_bound` 2 s: the calls in flight, then RST_STREAM CANCEL for each call that stays.\n * - `retrans_giveup_time` 924.6 s: the give-up of Linux at `tcp_retries2 = 15`.\n */\nexport const GRPC_CLIENT_STARTING_KNOBS: GrpcKnobs = {\n connect_bound: { value: 5, unit: \"s\" },\n request_timeout: { value: 9.5, unit: \"s\" },\n keepalive_time: { value: 300, unit: \"s\" },\n keepalive_timeout: { value: 20, unit: \"s\" },\n server_keepalive_min_time: { value: 300, unit: \"s\" },\n client_idle_timeout: { value: 60, unit: \"s\" },\n server_idle_close: { value: 75, unit: \"s\" },\n retry_base: { value: 1, unit: \"s\" },\n retry_cap: { value: 4, unit: \"s\" },\n keepalive_idle: { value: \"infinite\", unit: \"s\" },\n max_connection_age: { value: \"infinite\", unit: \"s\" },\n close_bound: { value: 2, unit: \"s\" },\n retrans_giveup_time: { value: 924.6, unit: \"s\" },\n};\n"],"mappings":"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"}
@@ -0,0 +1,248 @@
1
+ import { Y as Poll, wt as Operation } from "./lifecycle-fgYfxrHK.js";
2
+ import { C as MANIFESTATIONS, D as firstDetector, E as Situation, O as unwatched, S as Detector, T as Meeting, _ as Sent, a as ClientKnobs, c as ClientSpec, d as Factory, g as SendOptions, h as STARTING_POLL, i as ClientJoin, l as ClientStep, n as CheckOptions, o as ClientOptions, r as Client, s as ClientSocket, t as Attempt, u as DialFn, w as Manifestation } from "./core-BPLRDcUJ.js";
3
+ import { t as Judge } from "./dial-DA8vJfh2.js";
4
+ import { Extent, Quantity } from "@mark1russell7/quantity";
5
+ import { Machine } from "@mark1russell7/algebra";
6
+ //#region src/http-client/connection.d.ts
7
+ /** An HTTP socket is the socket of a client in the core: the join of the prober and the bounded close read it. */
8
+ type HttpSocket = ClientSocket;
9
+ /** An HTTP response is a whole response: its status, its headers, and its whole body. */
10
+ interface HttpResponse {
11
+ readonly status: number;
12
+ /** These are the headers of the response, by their names in lower case, as Node gives them. */
13
+ readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
14
+ readonly body: Buffer;
15
+ }
16
+ /** An agent request is one request that the wrapper gives the agent. */
17
+ interface AgentRequest {
18
+ readonly host: string;
19
+ readonly port: number;
20
+ readonly method: string;
21
+ readonly path: string;
22
+ readonly headers: Readonly<Record<string, string>>;
23
+ readonly body?: Uint8Array | undefined;
24
+ }
25
+ /**
26
+ * These are the events of one request that the wrapper reads. The agent gives each one at most one
27
+ * time. `connect` comes only for a new connection. A request on a free socket left the client when it got that socket.
28
+ */
29
+ interface AgentEvents {
30
+ /** The agent gave the request a socket. `reused` is true for a socket from the free list (`req.reusedSocket`). */
31
+ socket(socket: HttpSocket, reused: boolean): void;
32
+ /** The new socket of the request connected. The request then leaves the client. */
33
+ connect(): void;
34
+ }
35
+ /** An agent exchange is one request in the agent: the promise of its whole response, and the abort that destroys its socket. */
36
+ interface AgentExchange {
37
+ /** This promise resolves with the whole response, or rejects with the error of the request, the socket or the body. */
38
+ readonly response: Promise<HttpResponse>;
39
+ /** This method destroys the request and its socket. A request that waits for a socket leaves the queue of the agent. */
40
+ abort(error: Error): void;
41
+ }
42
+ /**
43
+ * An agent is the part of an `http.Agent` of Node that the wrapper uses, with `http.request` on it.
44
+ * A fake for tests has each part too. Thus the wrapper does not load node:http until it connects.
45
+ */
46
+ interface HttpAgent {
47
+ /** This method sends one request on the agent. */
48
+ request(request: AgentRequest, events: AgentEvents): AgentExchange;
49
+ /** This method gives each socket of the agent that is open: the busy ones and the free ones. */
50
+ sockets(): readonly HttpSocket[];
51
+ /** This method destroys each socket of the agent. A request on a socket then fails. */
52
+ destroy(): void;
53
+ }
54
+ /**
55
+ * These are the options that the wrapper gives `new http.Agent`. Each one comes from the declaration.
56
+ * The wrapper sets each option that has an effect on a timer, thus no default of Node decides one.
57
+ */
58
+ interface HttpAgentOptions {
59
+ readonly keepAlive: true;
60
+ /**
61
+ * This time is the free socket timer (`timeout`). The agent closes a free socket at the smaller of
62
+ * this time and the hint of the server less `agentKeepAliveTimeoutBuffer`. 0 is no timer, and then the hint does not apply.
63
+ */
64
+ readonly timeout: number;
65
+ /** This time is subtracted from the hint of the server (`Keep-Alive: timeout=…`). A hint at or below it gives no reuse. */
66
+ readonly agentKeepAliveTimeoutBuffer: number;
67
+ /** This time is the idle time of TCP keepalive. The agent always turns keepalive on, and Node truncates the time to whole seconds. */
68
+ readonly keepAliveMsecs: number;
69
+ /** This count is the most sockets for one origin. A request waits in the agent for a socket above it. */
70
+ readonly maxSockets: number;
71
+ /** This count is the most free sockets for one origin. */
72
+ readonly maxFreeSockets: number;
73
+ /** The agent gives the socket that was free last, as the global agent does. */
74
+ readonly scheduling: "lifo";
75
+ }
76
+ /** An agent factory makes the agent of the client from its options. */
77
+ type AgentFactory = Factory<HttpAgentOptions, HttpAgent>;
78
+ /**
79
+ * This factory makes an `http.Agent` of Node. It loads node:http only when a client connects.
80
+ * Each request goes through `http.request` with the agent. The response settles at the end of its body.
81
+ * A body that ends before it is complete rejects the response.
82
+ */
83
+ export declare const nodeAgent: AgentFactory;
84
+ //#endregion
85
+ //#region src/http-client/client.d.ts
86
+ /**
87
+ * These are the knobs of the client, each a time, by the names of the ordering law where the law
88
+ * has them. The runtime has no defaults, thus each knob is necessary. The constant
89
+ * `HTTP_CLIENT_STARTING_KNOBS` gives a declaration from the HTTP cells. The knobs that each client has come from the core.
90
+ */
91
+ interface HttpClientKnobs extends ClientKnobs {
92
+ /** This time bounds the TCP connect of a new socket. The wrapper has this timer, because the agent has none. */
93
+ readonly connect_bound: Quantity;
94
+ /**
95
+ * This time bounds each call: from its start until the end of the body of its response. It also
96
+ * bounds the wait for a socket. The wrapper has this timer, because the agent has none.
97
+ */
98
+ readonly request_timeout: Quantity;
99
+ /**
100
+ * This time is the free socket timer of the agent (`timeout`). The agent closes a free socket at the
101
+ * smaller of this time and the hint of the server less `hint_buffer`. It must be below `server_idle_close` (O01).
102
+ */
103
+ readonly pool_idle_expiry: Extent;
104
+ /** This time is what the agent subtracts from the hint of the server (`agentKeepAliveTimeoutBuffer`). A hint at or below it gives no reuse. */
105
+ readonly hint_buffer: Quantity;
106
+ /**
107
+ * This time is when the server closes an idle connection, after its response. A Node server closes
108
+ * at `keepAliveTimeout` plus `keepAliveTimeoutBuffer`. "infinite" is a server that keeps an idle
109
+ * connection with no bound. The client does not set it: `problems` compares it with `pool_idle_expiry` (O01).
110
+ */
111
+ readonly server_idle_close: Extent;
112
+ /** This time is the idle time of TCP keepalive (`keepAliveMsecs`), in whole seconds. The agent always turns keepalive on. */
113
+ readonly keepalive_idle: Extent;
114
+ /** The agent does not replace a connection at an age, thus this knob must be "infinite". */
115
+ readonly max_connection_age: Extent;
116
+ /** This time bounds the close of the client: the wait for the calls in flight, then a destroy of each socket. */
117
+ readonly close_bound: Quantity;
118
+ }
119
+ /** A check request is the request of the round-trip check, and of the first connection at the connect. */
120
+ interface CheckRequest {
121
+ readonly method: string;
122
+ readonly path: string;
123
+ }
124
+ /**
125
+ * This request asks about the server, not about a resource (RFC 9110 §9.3.7). Thus it changes no
126
+ * state. A server that does not route it still answers with a status, and the status tells that it is alive.
127
+ */
128
+ export declare const OPTIONS_STAR: CheckRequest;
129
+ /** An HTTP client spec is the declaration of one HTTP client: the origin that it connects to, its pool, and its knobs. */
130
+ interface HttpClientSpec extends ClientSpec<HttpClientKnobs> {
131
+ /** This count is the most sockets of the pool (`maxSockets`). It is also the most free sockets. */
132
+ readonly pool: {
133
+ readonly max: number;
134
+ };
135
+ /** This request is the request of the round-trip check and of the first connection. Without it, the client uses `OPTIONS *`. */
136
+ readonly check?: CheckRequest | undefined;
137
+ /**
138
+ * These operations add to the table of the client, or change it. The table names each method by
139
+ * its idempotency in RFC 9110. A caller can name an operation of its own, for example a POST with an `Idempotency-Key`.
140
+ */
141
+ readonly operations?: Readonly<Record<string, Operation>> | undefined;
142
+ }
143
+ /** These options make a client. Without them, it uses the agent of Node, the real clock, and a dial with `OPTIONS *`. */
144
+ type HttpClientOptions = ClientOptions<AgentFactory>;
145
+ /** An HTTP call is one request. A string body goes as UTF-8, with its `content-length`. */
146
+ interface HttpCall {
147
+ readonly method: string;
148
+ readonly path: string;
149
+ readonly headers?: Readonly<Record<string, string>> | undefined;
150
+ readonly body?: string | Uint8Array | undefined;
151
+ /** This name is the operation of the call in the table. Without it, the operation is the method. */
152
+ readonly operation?: string | undefined;
153
+ }
154
+ /** An HTTP client is a dependency of a service: its declaration, its handle, its checks and its requests. */
155
+ interface HttpClient extends Client<ClientStep, ClientJoin> {
156
+ /** This text is the state of the client, as `HTTP_CLIENT_MACHINE` names it. */
157
+ readonly status: string;
158
+ /**
159
+ * This method sends a request, and gives its outcome in three values. A whole response of each
160
+ * status is `ok`. The client sends again only an idempotent operation, one time at most, after an
161
+ * outcome that is not certain. The second try stays in the bound of the request.
162
+ */
163
+ request(call: HttpCall, options?: SendOptions): Promise<Sent<HttpResponse>>;
164
+ }
165
+ /**
166
+ * These operations are the methods of HTTP, by their idempotency in RFC 9110 §9.2.2. A second
167
+ * request of GET, HEAD, OPTIONS, TRACE, PUT or DELETE has the same effect as one. POST and PATCH do not.
168
+ */
169
+ export declare const HTTP_OPERATIONS: Readonly<Record<string, Operation>>;
170
+ /** This function gives the problems of a spec: the knobs that the agent cannot use. */
171
+ export declare const specProblems: (name: string, spec: HttpClientSpec) => string[];
172
+ /** This function gives the options of `new http.Agent` from a spec. Each timer of the agent comes from the declaration. */
173
+ export declare const agentOptions: (spec: HttpClientSpec) => HttpAgentOptions;
174
+ /** This function gives the bytes of the dial: `OPTIONS *` with the Host header, and a close after the answer. */
175
+ export declare const optionsRequest: (host: string, port: number) => string;
176
+ /** This judge reads the status line of the answer. Any status passes, because it tells that an HTTP server answered. */
177
+ export declare const statusJudge: Judge;
178
+ /** This dial function opens a TCP connection, sends `OPTIONS *`, and waits for the status line. It changes no state on the server. */
179
+ export declare const httpDial: DialFn;
180
+ /**
181
+ * This function makes an HTTP client, a dependency of a service over an `http.Agent` of Node.
182
+ * - It sets each timer of the agent from the declaration. The agent sends no request again by itself,
183
+ * and the client sends again only an idempotent operation of its table.
184
+ * - Its call is a request until the end of the body of its response. The wrapper has a timer for the
185
+ * connect and one for the call, because the agent has none.
186
+ * - It tells the outcome of a call in three values. A request that did not get a connected socket
187
+ * did not leave the client. A failure after that is not certain.
188
+ * - It destroys the socket of each call that is not `ok`, thus no later request gets that socket.
189
+ * - It records each new socket of the pool (D1). HTTP gives no id of a connection.
190
+ *
191
+ * The core of the clients gives the outcomes, the second try, the calls in flight and the timer of a
192
+ * call. It also gives the bounded close and the checks. This entry gives what the agent does in its own way.
193
+ */
194
+ export declare const httpClient: (name: string, spec: HttpClientSpec, options?: HttpClientOptions) => HttpClient;
195
+ //#endregion
196
+ //#region src/http-client/machine.d.ts
197
+ /**
198
+ * The HTTP client of the service as a `mark1russell7.machine/1` document. It is the agent as the
199
+ * wrapper sees it, not one socket: the document `node-http-agent` of `@mark1russell7/machines`
200
+ * gives each socket of the agent. The state is the outcome of the last connect of a socket:
201
+ * - `ready`: the last connect succeeded.
202
+ * - `failing`: the last connect failed, or did not end in connect_bound. The agent does not connect
203
+ * again by itself. The next request tries again.
204
+ *
205
+ * A free socket that the agent closes does not change the state. A request on a reused socket that
206
+ * fails records `reuse.failed`, and the state stays. A test records the transitions of the client
207
+ * and replays them against this document.
208
+ */
209
+ export declare const HTTP_CLIENT_MACHINE: Machine;
210
+ //#endregion
211
+ //#region src/http-client/meets.d.ts
212
+ /**
213
+ * This function gives the manifestations that an HTTP client can meet, and the detectors of each,
214
+ * from its knobs. The HTTP cells measured them on the agent of Node 24.21.0:
215
+ * - The agent has no timer of a call, thus the timer of the wrapper finds each silent call in flight.
216
+ * - The free socket timer closes an idle socket at `pool_idle_expiry` at most. Thus no request goes
217
+ * later on a socket that went silent while it was free.
218
+ * - A FIN on a busy socket fails the call with "socket hang up". A FIN on a free socket destroys it.
219
+ */
220
+ export declare const httpClientMeetings: (name: string, knobs: HttpClientKnobs) => Meeting[];
221
+ //#endregion
222
+ //#region src/http-client/starting.d.ts
223
+ /**
224
+ * This poll is the poll of the checks that `HTTP_CLIENT_STARTING_KNOBS` fits. It is `STARTING_POLL` of
225
+ * the core: each 5 s with a jitter of 20 %, and a timeout of 4 s (B6, O12). The poll does not depend on the driver.
226
+ */
227
+ export declare const HTTP_CLIENT_STARTING_POLL: Poll;
228
+ /**
229
+ * These knobs are a declaration to start from. They come from what the HTTP cells measured on the
230
+ * agent of Node 24.21.0 against a Node server at its defaults. The runtime has no defaults: a service gives
231
+ * these knobs, or its own. Each value holds the borders that the cells measured:
232
+ * - `connect_bound` 5 s: the connect timeout of DP44. The agent has no connect timer.
233
+ * - `request_timeout` 9.5 s: in the slowest poll of 10 s (O13). The agent has no timer of a call.
234
+ * A frozen server held the call of undici until its headersTimeout, at 10.187 s.
235
+ * - `pool_idle_expiry` 4 s: the free socket timer. The agent then closes a free socket at 4 s, the
236
+ * hint of 5 s less `hint_buffer`. That is below the idle close of the server.
237
+ * - `hint_buffer` 1 s: the default of the agent.
238
+ * - `server_idle_close` 6 s: a Node server closes an idle connection at its `keepAliveTimeout` of 5 s
239
+ * plus its buffer of 1 s. The cells measured 6.0003 s. O01 then has a margin of 2 s.
240
+ * - `keepalive_idle` 1 s: the default of the agent. Node truncates it to whole seconds.
241
+ * - `max_connection_age` "infinite": the agent does not replace a connection at an age.
242
+ * - `close_bound` 2 s: a call in flight behind a black hole holds its socket with no bound.
243
+ * - `retrans_giveup_time` 924.6 s: the give-up of Linux at `tcp_retries2 = 15`.
244
+ */
245
+ export declare const HTTP_CLIENT_STARTING_KNOBS: HttpClientKnobs;
246
+ //#endregion
247
+ export { type AgentEvents, type AgentExchange, type AgentFactory, type AgentRequest, type Attempt, type CheckOptions, type CheckRequest, type Client, type ClientJoin, type ClientKnobs, type ClientOptions, type ClientSocket, type ClientSpec, type ClientStep, type Detector, type DialFn, type Factory, type HttpAgent, type HttpAgentOptions, type HttpCall, type HttpClient, type HttpClientKnobs, type HttpClientOptions, type HttpClientSpec, type HttpResponse, type HttpSocket, MANIFESTATIONS, type Manifestation, type Meeting, STARTING_POLL, type SendOptions, type Sent, type Situation, firstDetector, unwatched };
248
+ //# sourceMappingURL=http-client.d.ts.map