@comunica/actor-init-query 2.3.0 → 2.3.1-alpha.23.0

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.
@@ -17,6 +17,10 @@
17
17
  {
18
18
  "@id": "caiq:components/HttpServiceSparqlEndpoint.jsonld#IQueryBody__member_value",
19
19
  "memberFieldName": "value"
20
+ },
21
+ {
22
+ "@id": "caiq:components/HttpServiceSparqlEndpoint.jsonld#IQueryBody__member_context",
23
+ "memberFieldName": "context"
20
24
  }
21
25
  ],
22
26
  "constructorArguments": []
@@ -51,6 +55,10 @@
51
55
  "@id": "caiq:components/HttpServiceSparqlEndpoint.jsonld#IHttpServiceSparqlEndpointArgs__member_freshWorkerPerQuery",
52
56
  "memberFieldName": "freshWorkerPerQuery"
53
57
  },
58
+ {
59
+ "@id": "caiq:components/HttpServiceSparqlEndpoint.jsonld#IHttpServiceSparqlEndpointArgs__member_contextOverride",
60
+ "memberFieldName": "contextOverride"
61
+ },
54
62
  {
55
63
  "@id": "caiq:components/HttpServiceSparqlEndpoint.jsonld#IHttpServiceSparqlEndpointArgs__member_moduleRootPath",
56
64
  "memberFieldName": "moduleRootPath"
@@ -21,6 +21,7 @@ export declare class HttpServiceSparqlEndpoint {
21
21
  readonly workers: number;
22
22
  readonly invalidateCacheBeforeQuery: boolean;
23
23
  readonly freshWorkerPerQuery: boolean;
24
+ readonly contextOverride: boolean;
24
25
  lastQueryId: number;
25
26
  constructor(args: IHttpServiceSparqlEndpointArgs);
26
27
  /**
@@ -108,6 +109,7 @@ export declare class HttpServiceSparqlEndpoint {
108
109
  export interface IQueryBody {
109
110
  type: 'query' | 'void';
110
111
  value: string;
112
+ context: Record<string, any> | undefined;
111
113
  }
112
114
  export interface IHttpServiceSparqlEndpointArgs extends IDynamicQueryEngineOptions {
113
115
  context?: any;
@@ -116,6 +118,7 @@ export interface IHttpServiceSparqlEndpointArgs extends IDynamicQueryEngineOptio
116
118
  workers?: number;
117
119
  invalidateCacheBeforeQuery?: boolean;
118
120
  freshWorkerPerQuery?: boolean;
121
+ contextOverride?: boolean;
119
122
  moduleRootPath: string;
120
123
  defaultConfigPath: string;
121
124
  }
@@ -25,6 +25,7 @@ class HttpServiceSparqlEndpoint {
25
25
  this.workers = args.workers ?? 1;
26
26
  this.invalidateCacheBeforeQuery = Boolean(args.invalidateCacheBeforeQuery);
27
27
  this.freshWorkerPerQuery = Boolean(args.freshWorkerPerQuery);
28
+ this.contextOverride = Boolean(args.contextOverride);
28
29
  this.engine = new __1.QueryEngineFactoryBase(args.moduleRootPath, args.defaultConfigPath, actorInitQuery => new __1.QueryEngineBase(actorInitQuery)).create(args);
29
30
  }
30
31
  /**
@@ -93,6 +94,7 @@ class HttpServiceSparqlEndpoint {
93
94
  }
94
95
  const invalidateCacheBeforeQuery = args.invalidateCache;
95
96
  const freshWorkerPerQuery = args.freshWorker;
97
+ const contextOverride = args.contextOverride;
96
98
  const port = args.port;
97
99
  const timeout = args.timeout * 1000;
98
100
  const workers = args.workers;
@@ -104,6 +106,7 @@ class HttpServiceSparqlEndpoint {
104
106
  context,
105
107
  invalidateCacheBeforeQuery,
106
108
  freshWorkerPerQuery,
109
+ contextOverride,
107
110
  moduleRootPath,
108
111
  mainModulePath: moduleRootPath,
109
112
  port,
@@ -138,8 +141,14 @@ class HttpServiceSparqlEndpoint {
138
141
  // Respawn crashed workers
139
142
  worker.once('exit', (code, signal) => {
140
143
  if (!worker.exitedAfterDisconnect) {
141
- stderr.write(`Worker ${worker.process.pid} died with ${code || signal}. Starting new worker.\n`);
142
- cluster.fork();
144
+ if (code === 9 || signal === 'SIGKILL') {
145
+ stderr.write(`Worker ${worker.process.pid} forcefully killed with ${code || signal}. Killing main process as well.\n`);
146
+ cluster.disconnect();
147
+ }
148
+ else {
149
+ stderr.write(`Worker ${worker.process.pid} died with ${code || signal}. Starting new worker.\n`);
150
+ cluster.fork();
151
+ }
143
152
  }
144
153
  });
145
154
  // Handle worker timeouts
@@ -147,9 +156,9 @@ class HttpServiceSparqlEndpoint {
147
156
  worker.on('message', ({ type, queryId }) => {
148
157
  if (type === 'start') {
149
158
  workerTimeouts[queryId] = setTimeout(() => {
150
- stderr.write(`Worker ${worker.process.pid} timed out for query ${queryId}.\n`);
151
159
  try {
152
160
  if (worker.isConnected()) {
161
+ stderr.write(`Worker ${worker.process.pid} timed out for query ${queryId}.\n`);
153
162
  worker.send('shutdown');
154
163
  }
155
164
  }
@@ -209,6 +218,19 @@ class HttpServiceSparqlEndpoint {
209
218
  process.exit(15);
210
219
  }
211
220
  });
221
+ // Catch global errors, and cleanly close open connections
222
+ process.on('uncaughtException', async (error) => {
223
+ stderr.write(`Terminating worker ${process.pid} with ${openConnections.size} open connections due to uncaught exception.\n`);
224
+ stderr.write(error.stack);
225
+ // Stop new connections from being accepted
226
+ server.close();
227
+ // Close all open connections
228
+ for (const connection of openConnections) {
229
+ await new Promise(resolve => connection.end('!ERROR!', resolve));
230
+ }
231
+ // Kill the worker once the connections have been closed
232
+ process.exit(15);
233
+ });
212
234
  }
213
235
  /**
214
236
  * Handles an HTTP request.
@@ -258,7 +280,7 @@ class HttpServiceSparqlEndpoint {
258
280
  case 'GET':
259
281
  // eslint-disable-next-line no-case-declarations
260
282
  const queryValue = requestUrl.query.query;
261
- queryBody = queryValue ? { type: 'query', value: queryValue } : undefined;
283
+ queryBody = queryValue ? { type: 'query', value: queryValue, context: undefined } : undefined;
262
284
  // eslint-disable-next-line no-case-declarations
263
285
  const headOnly = request.method === 'HEAD';
264
286
  await this.writeQueryResult(engine, stdout, stderr, request, response, queryBody, mediaType, headOnly, true, this.lastQueryId++);
@@ -287,7 +309,10 @@ class HttpServiceSparqlEndpoint {
287
309
  return this.writeServiceDescription(engine, stdout, stderr, request, response, mediaType, headOnly);
288
310
  }
289
311
  // Determine context
290
- let context = this.context;
312
+ let context = {
313
+ ...this.context,
314
+ ...this.contextOverride ? queryBody.context : undefined,
315
+ };
291
316
  if (readOnly) {
292
317
  context = { ...context, [context_entries_1.KeysQueryOperation.readOnly.name]: readOnly };
293
318
  }
@@ -334,7 +359,9 @@ class HttpServiceSparqlEndpoint {
334
359
  const { data } = await engine.resultToString(result, mediaType);
335
360
  data.on('error', (error) => {
336
361
  stdout.write(`[500] Server error in results: ${error.message} \n`);
337
- response.end('An internal server error occurred.\n');
362
+ if (!response.writableEnded) {
363
+ response.end('An internal server error occurred.\n');
364
+ }
338
365
  });
339
366
  data.pipe(response);
340
367
  eventEmitter = data;
@@ -450,18 +477,27 @@ class HttpServiceSparqlEndpoint {
450
477
  const contentType = request.headers['content-type'];
451
478
  if (contentType) {
452
479
  if (contentType.includes('application/sparql-query')) {
453
- return resolve({ type: 'query', value: body });
480
+ return resolve({ type: 'query', value: body, context: undefined });
454
481
  }
455
482
  if (contentType.includes('application/sparql-update')) {
456
- return resolve({ type: 'void', value: body });
483
+ return resolve({ type: 'void', value: body, context: undefined });
457
484
  }
458
485
  if (contentType.includes('application/x-www-form-urlencoded')) {
459
486
  const bodyStructure = querystring.parse(body);
487
+ let context;
488
+ if (bodyStructure.context) {
489
+ try {
490
+ context = JSON.parse(bodyStructure.context);
491
+ }
492
+ catch (error) {
493
+ reject(new Error(`Invalid POST body with context received ('${bodyStructure.context}'): ${error.message}`));
494
+ }
495
+ }
460
496
  if (bodyStructure.query) {
461
- return resolve({ type: 'query', value: bodyStructure.query });
497
+ return resolve({ type: 'query', value: bodyStructure.query, context });
462
498
  }
463
499
  if (bodyStructure.update) {
464
- return resolve({ type: 'void', value: bodyStructure.update });
500
+ return resolve({ type: 'void', value: bodyStructure.update, context });
465
501
  }
466
502
  }
467
503
  }
@@ -53,6 +53,11 @@ class CliArgsHandlerHttp {
53
53
  describe: 'Kills the worker after each query execution',
54
54
  default: false,
55
55
  },
56
+ contextOverride: {
57
+ type: 'boolean',
58
+ describe: 'If the query context can be overridden through POST requests',
59
+ default: false,
60
+ },
56
61
  })
57
62
  .check(args => {
58
63
  if (args.version) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comunica/actor-init-query",
3
- "version": "2.3.0",
3
+ "version": "2.3.1-alpha.23.0",
4
4
  "description": "A query init actor",
5
5
  "lsd:module": true,
6
6
  "main": "lib/index.js",
@@ -31,19 +31,19 @@
31
31
  "lib/**/*.js"
32
32
  ],
33
33
  "dependencies": {
34
- "@comunica/actor-http-proxy": "^2.3.0",
35
- "@comunica/bus-context-preprocess": "^2.3.0",
36
- "@comunica/bus-http-invalidate": "^2.3.0",
37
- "@comunica/bus-init": "^2.3.0",
38
- "@comunica/bus-optimize-query-operation": "^2.3.0",
39
- "@comunica/bus-query-operation": "^2.3.0",
40
- "@comunica/bus-query-parse": "^2.3.0",
41
- "@comunica/bus-query-result-serialize": "^2.3.0",
42
- "@comunica/context-entries": "^2.3.0",
43
- "@comunica/core": "^2.3.0",
44
- "@comunica/logger-pretty": "^2.3.0",
45
- "@comunica/runner": "^2.3.0",
46
- "@comunica/types": "^2.3.0",
34
+ "@comunica/actor-http-proxy": "2.3.1-alpha.23.0",
35
+ "@comunica/bus-context-preprocess": "2.3.1-alpha.23.0",
36
+ "@comunica/bus-http-invalidate": "2.3.1-alpha.23.0",
37
+ "@comunica/bus-init": "2.3.1-alpha.23.0",
38
+ "@comunica/bus-optimize-query-operation": "2.3.1-alpha.23.0",
39
+ "@comunica/bus-query-operation": "2.3.1-alpha.23.0",
40
+ "@comunica/bus-query-parse": "2.3.1-alpha.23.0",
41
+ "@comunica/bus-query-result-serialize": "2.3.1-alpha.23.0",
42
+ "@comunica/context-entries": "2.3.1-alpha.23.0",
43
+ "@comunica/core": "2.3.1-alpha.23.0",
44
+ "@comunica/logger-pretty": "2.3.1-alpha.23.0",
45
+ "@comunica/runner": "2.3.1-alpha.23.0",
46
+ "@comunica/types": "2.3.1-alpha.23.0",
47
47
  "@rdfjs/types": "*",
48
48
  "@types/yargs": "^17.0.2",
49
49
  "asynciterator": "^3.4.2",
@@ -62,5 +62,5 @@
62
62
  "browser": {
63
63
  "./lib/index.js": "./lib/index-browser.js"
64
64
  },
65
- "gitHead": "1e88a2053c15d75cc2e93c5161abdb24a4346c3d"
65
+ "gitHead": "4dd99fee904c64e9ef700eb5080197c4a03a36fa"
66
66
  }