@milaboratories/pframes-rs-serv 1.0.67 → 1.0.69

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.
@@ -7,6 +7,7 @@ export declare const StatusCode: {
7
7
  readonly Unauthorized: 401;
8
8
  readonly NotFound: 404;
9
9
  readonly MethodNotAllowed: 405;
10
+ readonly RequestTimeout: 408;
10
11
  readonly Gone: 410;
11
12
  readonly PreconditionFailed: 412;
12
13
  readonly RangeNotSatisfiable: 416;
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/utils/status.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAab,CAAC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/utils/status.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;CAcb,CAAC"}
@@ -7,6 +7,7 @@ const StatusCode = {
7
7
  Unauthorized: 401, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>
8
8
  NotFound: 404, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404>
9
9
  MethodNotAllowed: 405, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405>
10
+ RequestTimeout: 408, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408>
10
11
  Gone: 410, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410>
11
12
  PreconditionFailed: 412, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412>
12
13
  RangeNotSatisfiable: 416, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416>
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sources":["../../src/utils/status.ts"],"sourcesContent":["/** HTTP status codes used in the parquet server handler */\nexport const StatusCode = {\n Ok: 200, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200>\n PartialContent: 206, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206>\n NotModified: 304, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304>\n BadRequest: 400, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400>\n Unauthorized: 401, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>\n NotFound: 404, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404>\n MethodNotAllowed: 405, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405>\n Gone: 410, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410>\n PreconditionFailed: 412, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412>\n RangeNotSatisfiable: 416, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416>\n InternalServerError: 500, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500>\n GatewayTimeout: 504 // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504>\n} as const;\n"],"names":[],"mappings":"AAAA;AACO,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,GAAG;IACP,cAAc,EAAE,GAAG;IACnB,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,GAAG;IACf,YAAY,EAAE,GAAG;IACjB,QAAQ,EAAE,GAAG;IACb,gBAAgB,EAAE,GAAG;IACrB,IAAI,EAAE,GAAG;IACT,kBAAkB,EAAE,GAAG;IACvB,mBAAmB,EAAE,GAAG;IACxB,mBAAmB,EAAE,GAAG;IACxB,cAAc,EAAE,GAAG;;;;;"}
1
+ {"version":3,"file":"status.js","sources":["../../src/utils/status.ts"],"sourcesContent":["/** HTTP status codes used in the parquet server handler */\nexport const StatusCode = {\n Ok: 200, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200>\n PartialContent: 206, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206>\n NotModified: 304, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304>\n BadRequest: 400, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400>\n Unauthorized: 401, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>\n NotFound: 404, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404>\n MethodNotAllowed: 405, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405>\n RequestTimeout: 408, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408>\n Gone: 410, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410>\n PreconditionFailed: 412, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412>\n RangeNotSatisfiable: 416, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416>\n InternalServerError: 500, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500>\n GatewayTimeout: 504 // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504>\n} as const;\n"],"names":[],"mappings":"AAAA;AACO,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,GAAG;IACP,cAAc,EAAE,GAAG;IACnB,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,GAAG;IACf,YAAY,EAAE,GAAG;IACjB,QAAQ,EAAE,GAAG;IACb,gBAAgB,EAAE,GAAG;IACrB,cAAc,EAAE,GAAG;IACnB,IAAI,EAAE,GAAG;IACT,kBAAkB,EAAE,GAAG;IACvB,mBAAmB,EAAE,GAAG;IACxB,mBAAmB,EAAE,GAAG;IACxB,cAAc,EAAE,GAAG;;;;;"}
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "proxy",
9
9
  "server"
10
10
  ],
11
- "version": "1.0.67",
11
+ "version": "1.0.69",
12
12
  "type": "module",
13
13
  "types": "./dist/index.d.ts",
14
14
  "main": "./dist/index.js",
@@ -30,11 +30,12 @@
30
30
  "dependencies": {
31
31
  "@milaboratories/helpers": "1.6.22",
32
32
  "@milaboratories/pl-model-common": "1.19.14",
33
- "@milaboratories/pl-model-middle-layer": "1.8.17",
33
+ "@milaboratories/pl-model-middle-layer": "1.8.21",
34
34
  "commander": "^14.0.0",
35
35
  "selfsigned": "^3.0.1"
36
36
  },
37
37
  "devDependencies": {
38
+ "@datadog/pprof": "^5.9.0",
38
39
  "@milaboratories/ts-builder": "1.0.5",
39
40
  "@milaboratories/ts-configs": "1.0.6",
40
41
  "@types/autocannon": "^7.12.7",
@@ -43,7 +44,7 @@
43
44
  "autocannon": "^8.0.0",
44
45
  "tslib": "^2.8.1",
45
46
  "typescript": "^5.9.2",
46
- "undici": "^7.14.0",
47
+ "undici": "^7.15.0",
47
48
  "vite": "^7.1.3",
48
49
  "vitest": "^3.2.4"
49
50
  },
package/src/fs-store.ts CHANGED
@@ -3,7 +3,7 @@ import { createReadStream } from 'node:fs';
3
3
  import { stat, open, type FileHandle } from 'node:fs/promises';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
6
- import { ensureError } from '@milaboratories/pl-model-common';
6
+ import { ensureError, isAbortError } from '@milaboratories/pl-model-common';
7
7
 
8
8
  /** Object store for serving files from a local directory */
9
9
  export class FileSystemStore extends PFrameInternal.ObjectStore {
@@ -46,19 +46,20 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
46
46
  callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;
47
47
  }
48
48
  ): Promise<void> {
49
- let file: FileHandle;
49
+ let file: FileHandle | undefined;
50
50
  try {
51
- const path = join(this.rootDir, filename);
52
- file = await open(path, 'r');
53
- } catch (error: unknown) {
54
- this.logger(
55
- 'error',
56
- `File system store failed to open file ${filename}: ${ensureError(error)}`
57
- );
58
- return await params.callback({ type: 'NotFound' });
59
- }
51
+ try {
52
+ const path = join(this.rootDir, filename);
53
+ file = await open(path, 'r');
54
+ } catch (error: unknown) {
55
+ this.logger(
56
+ 'error',
57
+ `File system store failed to open file ${filename}: ${ensureError(error)}`
58
+ );
59
+ return await params.callback({ type: 'NotFound' });
60
+ }
61
+ params.signal.throwIfAborted();
60
62
 
61
- try {
62
63
  let size: number;
63
64
  try {
64
65
  ({ size } = await file.stat());
@@ -69,6 +70,7 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
69
70
  );
70
71
  return await params.callback({ type: 'InternalError' });
71
72
  }
73
+ params.signal.throwIfAborted();
72
74
 
73
75
  const range = this.translate(size, params.range);
74
76
  if (!range) {
@@ -83,7 +85,6 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
83
85
  try {
84
86
  data = createReadStream('ignored', {
85
87
  fd: file.fd,
86
- autoClose: false,
87
88
  start: range.start,
88
89
  end: range.end,
89
90
  signal: params.signal
@@ -100,11 +101,28 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
100
101
  return await params.callback({ type: 'InternalError' });
101
102
  }
102
103
 
103
- return await params
104
- .callback({ type: 'Ok', size, range, data })
105
- .catch(() => {});
104
+ try {
105
+ return await params.callback({ type: 'Ok', size, range, data });
106
+ } catch (error: unknown) {
107
+ if (!isAbortError(error)) {
108
+ this.logger(
109
+ 'error',
110
+ `File system store received unexpected rejection from callback: ${ensureError(error)}`
111
+ );
112
+ }
113
+ }
114
+ } catch (error: unknown) {
115
+ if (!isAbortError(error)) {
116
+ this.logger(
117
+ 'error',
118
+ `File system store unhandled error: ${ensureError(error)}`
119
+ );
120
+ }
121
+ return await params.callback({ type: 'InternalError' });
106
122
  } finally {
107
- await file.close();
123
+ await file?.close().catch(() => {
124
+ // already closed
125
+ });
108
126
  }
109
127
  }
110
128
  }
package/src/handler.ts CHANGED
@@ -18,13 +18,14 @@ import {
18
18
  HeaderName,
19
19
  HeaderValue
20
20
  } from './utils';
21
+ import { isAbortError } from '@milaboratories/pl-model-common';
21
22
 
22
23
  /** Main request handler for parquet files */
23
- async function handleRequest(
24
+ function handleRequest(
24
25
  request: IncomingMessage,
25
26
  response: ServerResponse,
26
27
  store: PFrameInternal.ObjectStore
27
- ): Promise<void> {
28
+ ): void {
28
29
  // RFC 9110 section 6.6.1: Date header should be present in all responses
29
30
  response.sendDate = true;
30
31
  // RFC 9110 section 8.6: Content-Length 0 as default for error responses
@@ -87,7 +88,11 @@ async function handleRequest(
87
88
  signal,
88
89
  // pipeline automatically destroys the streams if they were not gracefully closed
89
90
  callback: async (result) => {
90
- if (response.destroyed) return void response.destroy();
91
+ if (request.destroyed) {
92
+ // request has timed out, close the connection
93
+ response.setHeader(HeaderName.Connection, HeaderValue.Connection);
94
+ return void response.writeHead(StatusCode.RequestTimeout).end();
95
+ }
91
96
 
92
97
  switch (result.type) {
93
98
  case 'InternalError':
@@ -131,11 +136,11 @@ async function handleRequest(
131
136
  return void response.end();
132
137
  }
133
138
 
134
- return await pipeline(result.data!, response, { signal }).catch(() => {
135
- // Pipeline errors are expected when request is aborted or connection is lost
136
- // Response head was already written, so we can't change status code
137
- // Just mute the error - pipeline destroys the response stream
138
- });
139
+ try {
140
+ return await pipeline(result.data!, response, { signal });
141
+ } catch (error: unknown) {
142
+ if (!isAbortError(error)) throw error;
143
+ }
139
144
  }
140
145
  });
141
146
  }
@@ -153,7 +158,7 @@ export function createRequestHandler(
153
158
  options: PFrameInternal.RequestHandlerOptions
154
159
  ): RequestListener {
155
160
  const { store } = options;
156
- return (request, response) => void handleRequest(request, response, store);
161
+ return (request, response) => handleRequest(request, response, store);
157
162
  }
158
163
 
159
164
  /** Request authorization middleware */
@@ -2,23 +2,22 @@ import { type ChildProcess, spawn } from 'node:child_process';
2
2
  import { createInterface, type Interface } from 'node:readline/promises';
3
3
  import { join, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { Command } from 'commander';
6
- import { FileSystemStore } from './fs-store';
7
- import { createRequestHandler } from './handler';
8
- import { serve } from './serve';
5
+ import { Command, InvalidArgumentError } from 'commander';
6
+ import { HttpHelpers } from './export';
9
7
  import {
10
8
  parseJson,
11
- type StringifiedJson,
12
- stringifyJson
9
+ stringifyJson,
10
+ type StringifiedJson
13
11
  } from '@milaboratories/pl-model-common';
14
12
  import { type PFrameInternal } from '@milaboratories/pl-model-middle-layer';
15
13
 
16
14
  const Options = {
17
- Http: '--http',
18
- NoAuth: '--no-auth'
15
+ NoHttps: '--no-https',
16
+ NoAuth: '--no-auth',
17
+ Port: '--port'
19
18
  } as const;
20
19
 
21
- type Config = StringifiedJson<PFrameInternal.ParquetServerConfig>;
20
+ type Info = StringifiedJson<PFrameInternal.HttpServerInfo>;
22
21
 
23
22
  /**
24
23
  * Serves parquet files from the given root directory.
@@ -31,17 +30,36 @@ export async function runParquetServer(): Promise<void> {
31
30
  .name('parquet-server')
32
31
  .description('Serve parquet files from a directory over HTTP(S)')
33
32
  .argument('<root-directory>', 'Root directory containing parquet files')
34
- .option(Options.Http, 'Use HTTP instead of HTTPS', false)
35
- .option(Options.NoAuth, 'Disable authentication')
33
+ .option(Options.NoHttps, 'Downgrade HTTPS to HTTP')
34
+ .option(Options.NoAuth, 'Disable authorization')
35
+ .option(
36
+ `${Options.Port} <number>`,
37
+ 'Port to listen on',
38
+ (value) => {
39
+ const port = parseInt(value, 10);
40
+ if (isNaN(port) || port < 0 || port > 65535) {
41
+ throw new InvalidArgumentError('valid port numbers are 0-65535');
42
+ }
43
+ return port;
44
+ },
45
+ 0
46
+ )
36
47
  .action(
37
- async (rootDir: string, options: { http: boolean; auth: boolean }) => {
48
+ async (
49
+ rootDir: string,
50
+ options: {
51
+ https: boolean;
52
+ auth: boolean;
53
+ port: number;
54
+ }
55
+ ) => {
38
56
  const abortController = new AbortController();
39
57
  process
40
58
  .on('SIGINT', () => abortController.abort())
41
59
  .on('SIGTERM', () => abortController.abort());
42
60
  abortController.signal.throwIfAborted();
43
61
 
44
- const store = await FileSystemStore.init({
62
+ const store = await HttpHelpers.createFsStore({
45
63
  rootDir,
46
64
  logger: (level, message) => {
47
65
  const timestamp = new Date(Date.now()).toISOString();
@@ -49,22 +67,19 @@ export async function runParquetServer(): Promise<void> {
49
67
  }
50
68
  });
51
69
  abortController.signal.throwIfAborted();
52
- const handler = createRequestHandler({ store });
70
+ const handler = HttpHelpers.createRequestHandler({ store });
53
71
 
54
- const server = await serve({
72
+ const server = await HttpHelpers.createHttpServer({
55
73
  handler,
56
- ...(options.http && { http: true }),
57
- ...(!options.auth && { noAuth: true })
74
+ ...(!options.https && { noHttps: true }),
75
+ ...(!options.auth && { noAuth: true }),
76
+ port: options.port
58
77
  });
59
78
  abortController.signal.onabort = () => server.stop();
60
79
  abortController.signal.throwIfAborted();
61
80
 
62
- const serverConfig: Config = stringifyJson({
63
- url: server.address,
64
- ...(server.authToken && { authToken: server.authToken }),
65
- ...(server.encodedCaCert && { caCert: server.encodedCaCert })
66
- });
67
- console.log(serverConfig);
81
+ const serverInfo: Info = stringifyJson(server.info);
82
+ console.log(serverInfo);
68
83
 
69
84
  await server.stopped;
70
85
  }
@@ -81,32 +96,32 @@ export async function runParquetServer(): Promise<void> {
81
96
  */
82
97
  export class ParquetServer implements Disposable {
83
98
  readonly #process: ChildProcess;
84
- readonly #config: PFrameInternal.ParquetServerConfig;
99
+ readonly #info: PFrameInternal.HttpServerInfo;
85
100
  readonly #lineReader: Interface;
86
101
 
87
102
  private constructor(
88
103
  process: ChildProcess,
89
- config: PFrameInternal.ParquetServerConfig,
104
+ info: PFrameInternal.HttpServerInfo,
90
105
  lineReader: Interface
91
106
  ) {
92
107
  this.#process = process;
93
- this.#config = config;
108
+ this.#info = info;
94
109
  this.#lineReader = lineReader;
95
110
  }
96
111
 
97
- get config(): PFrameInternal.ParquetServerConfig {
98
- return this.#config;
112
+ get info(): PFrameInternal.HttpServerInfo {
113
+ return this.#info;
99
114
  }
100
115
 
101
116
  static async serve(
102
117
  rootDir: string,
103
118
  options?: {
104
- http?: boolean;
105
- noAuth?: boolean;
119
+ noHttps?: true;
120
+ noAuth?: true;
121
+ port?: number;
106
122
  }
107
123
  ): Promise<ParquetServer> {
108
- const nodeFileUrl = import.meta.url;
109
- const nodeDirname = dirname(fileURLToPath(nodeFileUrl));
124
+ const nodeDirname = dirname(fileURLToPath(import.meta.url));
110
125
  const binPath = join(nodeDirname, '..', 'bin', 'parquet-server.mjs');
111
126
 
112
127
  const serverProcess = spawn(
@@ -114,8 +129,9 @@ export class ParquetServer implements Disposable {
114
129
  [
115
130
  binPath,
116
131
  rootDir,
117
- ...(options?.http ? [Options.Http] : []),
118
- ...(options?.noAuth ? [Options.NoAuth] : [])
132
+ ...(options?.noHttps ? [Options.NoHttps] : []),
133
+ ...(options?.noAuth ? [Options.NoAuth] : []),
134
+ ...(options?.port ? [Options.Port, options.port.toString()] : [])
119
135
  ],
120
136
  {
121
137
  stdio: ['ignore', 'pipe', 'ignore']
@@ -125,11 +141,11 @@ export class ParquetServer implements Disposable {
125
141
  const lineReader = createInterface({ input: serverProcess.stdout! });
126
142
 
127
143
  const firstLine = await lineReader[Symbol.asyncIterator]().next();
128
- const serverConfig = parseJson(firstLine.value as Config);
144
+ const serverInfo = parseJson(firstLine.value as Info);
129
145
 
130
146
  lineReader.on('line', console.log);
131
147
 
132
- return new ParquetServer(serverProcess, serverConfig, lineReader);
148
+ return new ParquetServer(serverProcess, serverInfo, lineReader);
133
149
  }
134
150
 
135
151
  [Symbol.dispose](): void {
package/src/serve.ts CHANGED
@@ -14,8 +14,7 @@ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
14
14
  import {
15
15
  base64Encode,
16
16
  Base64Encoded,
17
- ensureError,
18
- PFrameError
17
+ ensureError
19
18
  } from '@milaboratories/pl-model-common';
20
19
  import { generate, type GenerateResult } from 'selfsigned';
21
20
  import { randomUUID } from 'node:crypto';
@@ -54,18 +53,16 @@ async function generateCertificate(): Promise<GenerateResult> {
54
53
  /** Create an object store URL from the server address info. */
55
54
  function createObjectStoreUrl(
56
55
  info: AddressInfo,
57
- http?: true
56
+ noHttps?: true
58
57
  ): PFrameInternal.ObjectStoreUrl {
59
- const protocol = http ? 'http' : 'https';
58
+ const protocol = noHttps ? 'http' : 'https';
60
59
  switch (info.family) {
61
60
  case 'IPv4':
62
61
  return `${protocol}://${info.address}:${info.port}/` as PFrameInternal.ObjectStoreUrl;
63
62
  case 'IPv6':
64
63
  return `${protocol}://[${info.address}]:${info.port}/` as PFrameInternal.ObjectStoreUrl;
65
64
  default:
66
- throw new PFrameError(
67
- `PFrame helper HTTP(S) server bound to 'localhost' has unknown address family: ${info.family}`
68
- );
65
+ return `${protocol}://localhost:${info.port}/` as PFrameInternal.ObjectStoreUrl;
69
66
  }
70
67
  }
71
68
 
@@ -76,7 +73,7 @@ function createObjectStoreUrl(
76
73
  export async function serve({
77
74
  handler,
78
75
  port = 0,
79
- http,
76
+ noHttps,
80
77
  noAuth
81
78
  }: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer> {
82
79
  const started = new Deferred<PFrameInternal.HttpServer>();
@@ -91,52 +88,41 @@ export async function serve({
91
88
  }
92
89
 
93
90
  // Create HTTP server
94
- let certificateBase64:
95
- | Base64Encoded<PFrameInternal.PemCertificate>
96
- | undefined;
91
+ let encodedCaCert: Base64Encoded<PFrameInternal.PemCertificate> | undefined;
97
92
  const defaultOptions: ServerOptions = {
98
93
  keepAlive: true
99
94
  };
100
95
  let server: HttpServer | HttpsServer;
101
96
 
102
- if (http) {
97
+ if (noHttps) {
103
98
  server = createHttpServer(defaultOptions, effectiveHandler);
104
99
  } else {
105
100
  const { cert, private: key, public: ca } = await generateCertificate();
106
- certificateBase64 = base64Encode(cert as PFrameInternal.PemCertificate);
101
+ encodedCaCert = base64Encode(cert as PFrameInternal.PemCertificate);
107
102
  server = createHttpsServer(
108
103
  { ...defaultOptions, cert, key, ca },
109
104
  effectiveHandler
110
105
  );
111
106
  }
112
107
 
113
- const abortController = new AbortController();
114
108
  server
115
109
  .on('listening', () => {
116
110
  // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
117
- const address = createObjectStoreUrl(
111
+ const url = createObjectStoreUrl(
118
112
  server.address() as AddressInfo,
119
- http
113
+ noHttps
120
114
  );
121
115
  stopped = new Deferred<void>();
122
116
 
123
117
  started.resolve({
124
- get address(): PFrameInternal.ObjectStoreUrl {
125
- return address;
126
- },
127
- get authToken(): PFrameInternal.HttpAuthorizationToken | undefined {
128
- return authToken;
129
- },
130
- get encodedCaCert():
131
- | Base64Encoded<PFrameInternal.PemCertificate>
132
- | undefined {
133
- return certificateBase64;
118
+ get info(): PFrameInternal.HttpServerInfo {
119
+ return { url, authToken, encodedCaCert };
134
120
  },
135
121
  get stopped(): Promise<void> {
136
122
  return stopped!.promise;
137
123
  },
138
124
  stop(): Promise<void> {
139
- abortController.abort();
125
+ server.close();
140
126
  return stopped!.promise;
141
127
  }
142
128
  });
@@ -148,8 +134,7 @@ export async function serve({
148
134
  .on('close', () => stopped?.resolve())
149
135
  .listen({
150
136
  host: 'localhost',
151
- port,
152
- signal: abortController.signal
137
+ port
153
138
  });
154
139
  } catch (error: unknown) {
155
140
  started.reject(ensureError(error));
@@ -5,6 +5,7 @@ export const HeaderName = {
5
5
  Allow: 'allow',
6
6
  Authorization: 'authorization',
7
7
  CacheControl: 'cache-control',
8
+ Connection: 'connection',
8
9
  ContentLength: 'content-length',
9
10
  ContentRange: 'content-range',
10
11
  ContentType: 'content-type',
@@ -24,6 +25,7 @@ export const HeaderValue = {
24
25
  AcceptRanges: 'bytes',
25
26
  Allow: 'GET, HEAD',
26
27
  CacheControl: 'public, immutable, max-age=31536000',
28
+ Connection: 'close',
27
29
  ContentType: 'application/octet-stream',
28
30
  WWWAuthenticate: 'Bearer realm="parquet-server"'
29
31
  } as const;
@@ -7,6 +7,7 @@ export const StatusCode = {
7
7
  Unauthorized: 401, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>
8
8
  NotFound: 404, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404>
9
9
  MethodNotAllowed: 405, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405>
10
+ RequestTimeout: 408, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408>
10
11
  Gone: 410, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410>
11
12
  PreconditionFailed: 412, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412>
12
13
  RangeNotSatisfiable: 416, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416>