presidium-db-client-server 0.5.0 → 0.6.1

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.
@@ -127,7 +127,11 @@ function DatabaseHandler(options) {
127
127
  },
128
128
 
129
129
  eq("set"), async () => {
130
- const { key, value, sortValue } = await Readable.JSON(request)
130
+ const key = request.headers["key"]
131
+ const sortValue = request.headers["sort-value"]
132
+ const value = request.headers["content-type"] == "text/plain"
133
+ ? await Readable.Text(request)
134
+ : await Readable.Buffer(request)
131
135
  await ht.set(key, value, sortValue)
132
136
  response.writeHead(200, {
133
137
  "Content-Type": "text/plain",
@@ -136,7 +140,7 @@ function DatabaseHandler(options) {
136
140
  },
137
141
 
138
142
  eq("get"), async () => {
139
- const { key } = await Readable.JSON(request)
143
+ const key = request.headers["key"]
140
144
  const value = await ht.get(key)
141
145
  if (value === undefined) {
142
146
  response.writeHead(499, {
@@ -152,7 +156,7 @@ function DatabaseHandler(options) {
152
156
  },
153
157
 
154
158
  eq("getBinary"), async () => {
155
- const { key } = await Readable.JSON(request)
159
+ const key = request.headers["key"]
156
160
  const value = await ht.getBinary(key)
157
161
  response.writeHead(200, {
158
162
  "Content-Type": "application/octet-stream",
@@ -161,7 +165,7 @@ function DatabaseHandler(options) {
161
165
  },
162
166
 
163
167
  eq("delete"), async () => {
164
- const { key } = await Readable.JSON(request)
168
+ const key = request.headers["key"]
165
169
  const didDelete = await ht.delete(key)
166
170
  response.writeHead(200, {
167
171
  "Content-Type": "text/plain",
@@ -178,7 +182,8 @@ function DatabaseHandler(options) {
178
182
  },
179
183
 
180
184
  eq("iterator"), async () => {
181
- const options = await Readable.JSON(request)
185
+ const valueType = request.headers["value-type"]
186
+ const options = filter({ valueType }, __)
182
187
  response.writeHead(200, {
183
188
  "Content-Type": "application/octet-stream",
184
189
  "Content-Encoding": "utf8",
@@ -192,7 +197,8 @@ function DatabaseHandler(options) {
192
197
  },
193
198
 
194
199
  eq("entriesIterator"), async () => {
195
- const options = await Readable.JSON(request)
200
+ const valueType = request.headers["value-type"]
201
+ const options = filter({ valueType }, __)
196
202
  response.writeHead(200, {
197
203
  "Content-Type": "application/octet-stream",
198
204
  "Content-Encoding": "utf8",
@@ -208,7 +214,17 @@ function DatabaseHandler(options) {
208
214
  },
209
215
 
210
216
  eq("forwardIterator"), async () => {
211
- const options = await Readable.JSON(request)
217
+ const valueType = request.headers["value-type"]
218
+ const exclusiveStartKey = request.headers["exclusive-start-key"]
219
+ const startingSortValue = request.headers["starting-sort-value"]
220
+ const endingSortValue = request.headers["ending-sort-value"]
221
+ const options = filter({
222
+ valueType,
223
+ exclusiveStartKey,
224
+ startingSortValue,
225
+ endingSortValue,
226
+ }, __)
227
+
212
228
  response.writeHead(200, {
213
229
  "Content-Type": "application/octet-stream",
214
230
  "Content-Encoding": "utf8",
@@ -222,7 +238,17 @@ function DatabaseHandler(options) {
222
238
  },
223
239
 
224
240
  eq("reverseIterator"), async () => {
225
- const options = await Readable.JSON(request)
241
+ const valueType = request.headers["value-type"]
242
+ const exclusiveStartKey = request.headers["exclusive-start-key"]
243
+ const startingSortValue = request.headers["starting-sort-value"]
244
+ const endingSortValue = request.headers["ending-sort-value"]
245
+ const options = filter({
246
+ valueType,
247
+ exclusiveStartKey,
248
+ startingSortValue,
249
+ endingSortValue,
250
+ }, __)
251
+
226
252
  response.writeHead(200, {
227
253
  "Content-Type": "application/octet-stream",
228
254
  "Content-Encoding": "utf8",
@@ -236,7 +262,17 @@ function DatabaseHandler(options) {
236
262
  },
237
263
 
238
264
  eq("forwardEntriesIterator"), async () => {
239
- const options = await Readable.JSON(request)
265
+ const valueType = request.headers["value-type"]
266
+ const exclusiveStartKey = request.headers["exclusive-start-key"]
267
+ const startingSortValue = request.headers["starting-sort-value"]
268
+ const endingSortValue = request.headers["ending-sort-value"]
269
+ const options = filter({
270
+ valueType,
271
+ exclusiveStartKey,
272
+ startingSortValue,
273
+ endingSortValue,
274
+ }, __)
275
+
240
276
  response.writeHead(200, {
241
277
  "Content-Type": "application/octet-stream",
242
278
  "Content-Encoding": "utf8",
@@ -254,7 +290,17 @@ function DatabaseHandler(options) {
254
290
  },
255
291
 
256
292
  eq("reverseEntriesIterator"), async () => {
257
- const options = await Readable.JSON(request)
293
+ const valueType = request.headers["value-type"]
294
+ const exclusiveStartKey = request.headers["exclusive-start-key"]
295
+ const startingSortValue = request.headers["starting-sort-value"]
296
+ const endingSortValue = request.headers["ending-sort-value"]
297
+ const options = filter({
298
+ valueType,
299
+ exclusiveStartKey,
300
+ startingSortValue,
301
+ endingSortValue,
302
+ }, __)
303
+
258
304
  response.writeHead(200, {
259
305
  "Content-Type": "application/octet-stream",
260
306
  "Content-Encoding": "utf8",
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Presidium DB Client and Server may be freely distributed under the CFOSS license.
3
+ */
4
+
5
+ require("rubico/global")
6
+ const HTTP = require("presidium/HTTP")
7
+ const loadBalancerHandler = require("./loadBalancerHandler")
8
+ const healthCheckHandler = require("./healthCheckHandler")
9
+ const notFoundHandler = require("./notFoundHandler")
10
+ const errorHandler = require("./errorHandler")
11
+
12
+ /**
13
+ * @name DatabaseLoadbalancer
14
+ *
15
+ * @docs
16
+ * ```specscript
17
+ * module HTTP "https://presidium.services/docs/HTTP"
18
+ *
19
+ * HTTP.Handler(HTTP.Request, HTTP.Response) -> ()
20
+ *
21
+ * new DatabaseLoadbalancer(
22
+ * hash(tableName string, key string, length number) -> Promise|number,
23
+ * options {
24
+ * serverIps: Array<string>,
25
+ * },
26
+ * ...handlers Arguments<HTTP.Handler>
27
+ * )
28
+ * ```
29
+ */
30
+ class DatabaseLoadbalancer {
31
+ constructor(hash, options, ...handlers) {
32
+ this.hash = hash
33
+ this.serverIps = options.serverIps
34
+ this.handlers = handlers
35
+ this.clients = new Map()
36
+ this.closed = false
37
+
38
+ const router = tryCatch(
39
+ switchCase(
40
+ request => request.path == "/health",
41
+ healthCheckHandler,
42
+
43
+ request => request.pathDepth == 2,
44
+ curry.call(loadBalancerHandler, this),
45
+
46
+ notFoundHandler,
47
+ ),
48
+ errorHandler
49
+ )
50
+
51
+ const handler = all.series([
52
+ ...this.handlers,
53
+ request => {
54
+ const { pathname } = new URL(`http://*${request.url}`)
55
+ request.path = decodeURIComponent(
56
+ pathname == "/" ? pathname
57
+ : pathname.endsWith("/") ? pathname.slice(0, -1)
58
+ : pathname
59
+ )
60
+ request.pathSegments = request.path.slice(1).split("/")
61
+ request.pathDepth = request.pathSegments.length
62
+ },
63
+ router,
64
+ ])
65
+
66
+ this.server = HTTP.Server(handler)
67
+ }
68
+
69
+ /**
70
+ * @name listen
71
+ *
72
+ * @docs
73
+ * ```specscript
74
+ * listen(port number) -> ()
75
+ * listen(port number, callback function) -> ()
76
+ * ```
77
+ */
78
+ listen(port, callback) {
79
+ this.server.listen(port, callback)
80
+ }
81
+
82
+ /**
83
+ * @name close
84
+ *
85
+ * @docs
86
+ * ```specscript
87
+ * close() -> ()
88
+ * ```
89
+ */
90
+ close() {
91
+ this.server.close()
92
+ this.closed = true
93
+ }
94
+
95
+ }
96
+
97
+ module.exports = DatabaseLoadbalancer
package/DatabaseServer.js CHANGED
@@ -9,6 +9,7 @@ const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
9
9
  const DatabaseHandler = require("./DatabaseHandler")
10
10
  const healthCheckHandler = require("./healthCheckHandler")
11
11
  const notFoundHandler = require("./notFoundHandler")
12
+ const errorHandler = require("./errorHandler")
12
13
 
13
14
  /**
14
15
  * @name DatabaseServer
@@ -84,12 +85,7 @@ class DatabaseServer {
84
85
 
85
86
  notFoundHandler,
86
87
  ),
87
- function errorHandler(error, request, response) {
88
- response.writeHead(error.code ?? 500, {
89
- "Content-Type": "text/plain",
90
- })
91
- response.end(error.message)
92
- }
88
+ errorHandler
93
89
  )
94
90
 
95
91
  const handler = all.series([
@@ -41,6 +41,7 @@ class DiskHashTableClient {
41
41
  async clear() {
42
42
  const response = await this.http.POST(`/${this.tableName}/clear`, {
43
43
  headers: {
44
+ "Content-Type": "text/plain",
44
45
  "Client-Name": "DiskHashTableClient",
45
46
  },
46
47
  })
@@ -60,10 +61,13 @@ class DiskHashTableClient {
60
61
  async set(key, value) {
61
62
  const response = await this.http.POST(`/${this.tableName}/set`, {
62
63
  headers: {
63
- "Content-Type": "application/json",
64
+ "Content-Type": Buffer.isBuffer(value)
65
+ ? "application/octet-stream"
66
+ : "text/plain",
64
67
  "Client-Name": "DiskHashTableClient",
68
+ "Key": key,
65
69
  },
66
- body: JSON.stringify({ key, value }),
70
+ body: value,
67
71
  })
68
72
  await handleServerResponse(response)
69
73
  }
@@ -81,10 +85,10 @@ class DiskHashTableClient {
81
85
  async get(key) {
82
86
  const response = await this.http.POST(`/${this.tableName}/get`, {
83
87
  headers: {
84
- "Content-Type": "application/json",
88
+ "Content-Type": "text/plain",
85
89
  "Client-Name": "DiskHashTableClient",
90
+ "Key": key,
86
91
  },
87
- body: JSON.stringify({ key }),
88
92
  })
89
93
  if (response.status == 499) {
90
94
  return undefined
@@ -105,10 +109,10 @@ class DiskHashTableClient {
105
109
  async getBinary(key) {
106
110
  const response = await this.http.POST(`/${this.tableName}/getBinary`, {
107
111
  headers: {
108
- "Content-Type": "application/json",
112
+ "Content-Type": "text/plain",
109
113
  "Client-Name": "DiskHashTableClient",
114
+ "Key": key,
110
115
  },
111
- body: JSON.stringify({ key }),
112
116
  })
113
117
  return handleServerResponse(response)
114
118
  }
@@ -126,10 +130,10 @@ class DiskHashTableClient {
126
130
  async delete(key) {
127
131
  const response = await this.http.POST(`/${this.tableName}/delete`, {
128
132
  headers: {
129
- "Content-Type": "application/json",
133
+ "Content-Type": "text/plain",
130
134
  "Client-Name": "DiskHashTableClient",
135
+ "Key": key,
131
136
  },
132
- body: JSON.stringify({ key }),
133
137
  })
134
138
  const didDeleteText = await handleServerResponse(response)
135
139
  return didDeleteText == "true"
@@ -146,6 +150,7 @@ class DiskHashTableClient {
146
150
  async count() {
147
151
  const response = await this.http.POST(`/${this.tableName}/count`, {
148
152
  headers: {
153
+ "Content-Type": "text/plain",
149
154
  "Client-Name": "DiskHashTableClient",
150
155
  },
151
156
  })
@@ -167,10 +172,10 @@ class DiskHashTableClient {
167
172
  const { valueType } = options
168
173
  const response = await this.http.POST(`/${this.tableName}/iterator`, {
169
174
  headers: {
170
- "Content-Type": "application/json",
175
+ "Content-Type": "text/plain",
171
176
  "Client-Name": "DiskHashTableClient",
177
+ ...valueType ? { "Value-Type": valueType } : {},
172
178
  },
173
- body: JSON.stringify(options),
174
179
  })
175
180
  if (valueType == "binary") {
176
181
  return handleServerResponse(response, {
@@ -198,10 +203,10 @@ class DiskHashTableClient {
198
203
  const { valueType } = options
199
204
  const response = await this.http.POST(`/${this.tableName}/entriesIterator`, {
200
205
  headers: {
201
- "Content-Type": "application/json",
206
+ "Content-Type": "text/plain",
202
207
  "Client-Name": "DiskHashTableClient",
208
+ ...valueType ? { "Value-Type": valueType } : {},
203
209
  },
204
- body: JSON.stringify({ valueType }),
205
210
  })
206
211
 
207
212
  if (valueType == "binary") {
@@ -6,6 +6,7 @@ require("rubico/global")
6
6
  const HTTP = require("presidium/HTTP")
7
7
  const handleServerResponse = require("./handleServerResponse")
8
8
  const buffersToEntry = require("./buffersToEntry")
9
+ const parseString = require("./parseString")
9
10
 
10
11
  /**
11
12
  * @name DiskSortedHashTableClient
@@ -44,13 +45,14 @@ class DiskSortedHashTableClient {
44
45
  async clear() {
45
46
  const response = await this.http.POST(`/${this.tableName}/clear`, {
46
47
  headers: {
48
+ "Content-Type": "text/plain",
47
49
  "Client-Name": "DiskSortedHashTableClient",
48
- ...this.sortValueType ? {
49
- "Sort-Value-Type": this.sortValueType,
50
- } : {},
51
- ...this.degree ? {
52
- "Degree": Number(this.degree),
53
- } : {},
50
+
51
+ ...pipe(this, all({
52
+ "Sort-Value-Type": get("sortValueType"),
53
+ "Degree": get("degree"),
54
+ }), filter(__), map(parseString)),
55
+
54
56
  },
55
57
  })
56
58
  return handleServerResponse(response)
@@ -69,16 +71,20 @@ class DiskSortedHashTableClient {
69
71
  async set(key, value, sortValue) {
70
72
  const response = await this.http.POST(`/${this.tableName}/set`, {
71
73
  headers: {
72
- "Content-Type": "application/json",
74
+ "Content-Type": Buffer.isBuffer(value)
75
+ ? "application/octet-stream"
76
+ : "text/plain",
73
77
  "Client-Name": "DiskSortedHashTableClient",
74
- ...this.sortValueType ? {
75
- "Sort-Value-Type": this.sortValueType,
76
- } : {},
77
- ...this.degree ? {
78
- "Degree": Number(this.degree),
79
- } : {},
78
+ "Key": key,
79
+ "Sort-Value": sortValue,
80
+
81
+ ...pipe(this, all({
82
+ "Sort-Value-Type": get("sortValueType"),
83
+ "Degree": get("degree"),
84
+ }), filter(__), map(parseString)),
85
+
80
86
  },
81
- body: JSON.stringify({ key, value, sortValue }),
87
+ body: value,
82
88
  })
83
89
  await handleServerResponse(response)
84
90
  }
@@ -96,16 +102,16 @@ class DiskSortedHashTableClient {
96
102
  async get(key) {
97
103
  const response = await this.http.POST(`/${this.tableName}/get`, {
98
104
  headers: {
99
- "Content-Type": "application/json",
105
+ "Content-Type": "text/plain",
100
106
  "Client-Name": "DiskSortedHashTableClient",
101
- ...this.sortValueType ? {
102
- "Sort-Value-Type": this.sortValueType,
103
- } : {},
104
- ...this.degree ? {
105
- "Degree": Number(this.degree),
106
- } : {},
107
+ "Key": key,
108
+
109
+ ...pipe(this, all({
110
+ "Sort-Value-Type": get("sortValueType"),
111
+ "Degree": get("degree"),
112
+ }), filter(__), map(parseString)),
113
+
107
114
  },
108
- body: JSON.stringify({ key }),
109
115
  })
110
116
  if (response.status == 499) {
111
117
  return undefined
@@ -126,16 +132,16 @@ class DiskSortedHashTableClient {
126
132
  async getBinary(key) {
127
133
  const response = await this.http.POST(`/${this.tableName}/getBinary`, {
128
134
  headers: {
129
- "Content-Type": "application/json",
135
+ "Content-Type": "text/plain",
130
136
  "Client-Name": "DiskSortedHashTableClient",
131
- ...this.sortValueType ? {
132
- "Sort-Value-Type": this.sortValueType,
133
- } : {},
134
- ...this.degree ? {
135
- "Degree": Number(this.degree),
136
- } : {},
137
+ "Key": key,
138
+
139
+ ...pipe(this, all({
140
+ "Sort-Value-Type": get("sortValueType"),
141
+ "Degree": get("degree"),
142
+ }), filter(__), map(parseString)),
143
+
137
144
  },
138
- body: JSON.stringify({ key }),
139
145
  })
140
146
  return handleServerResponse(response)
141
147
  }
@@ -157,16 +163,22 @@ class DiskSortedHashTableClient {
157
163
  const { valueType } = options
158
164
  const response = await this.http.POST(`/${this.tableName}/forwardIterator`, {
159
165
  headers: {
160
- "Content-Type": "application/json",
166
+ "Content-Type": "text/plain",
161
167
  "Client-Name": "DiskSortedHashTableClient",
162
- ...this.sortValueType ? {
163
- "Sort-Value-Type": this.sortValueType,
164
- } : {},
165
- ...this.degree ? {
166
- "Degree": Number(this.degree),
167
- } : {},
168
+
169
+ ...pipe(options, all({
170
+ "Exclusive-Start-Key": get("exclusiveStartKey"),
171
+ "Starting-Sort-Value": get("startingSortValue"),
172
+ "Ending-Sort-Value": get("endingSortValue"),
173
+ "Value-Type": get("valueType"),
174
+ }), filter(__), map(parseString)),
175
+
176
+ ...pipe(this, all({
177
+ "Sort-Value-Type": get("sortValueType"),
178
+ "Degree": get("degree"),
179
+ }), filter(__), map(parseString)),
180
+
168
181
  },
169
- body: JSON.stringify(options),
170
182
  })
171
183
  if (valueType == "binary") {
172
184
  return handleServerResponse(response, {
@@ -197,16 +209,22 @@ class DiskSortedHashTableClient {
197
209
  const { valueType } = options
198
210
  const response = await this.http.POST(`/${this.tableName}/reverseIterator`, {
199
211
  headers: {
200
- "Content-Type": "application/json",
212
+ "Content-Type": "text/plain",
201
213
  "Client-Name": "DiskSortedHashTableClient",
202
- ...this.sortValueType ? {
203
- "Sort-Value-Type": this.sortValueType,
204
- } : {},
205
- ...this.degree ? {
206
- "Degree": Number(this.degree),
207
- } : {},
214
+
215
+ ...pipe(options, all({
216
+ "Exclusive-Start-Key": get("exclusiveStartKey"),
217
+ "Starting-Sort-Value": get("startingSortValue"),
218
+ "Ending-Sort-Value": get("endingSortValue"),
219
+ "Value-Type": get("valueType"),
220
+ }), filter(__), map(parseString)),
221
+
222
+ ...pipe(this, all({
223
+ "Sort-Value-Type": get("sortValueType"),
224
+ "Degree": get("degree"),
225
+ }), filter(__), map(parseString)),
226
+
208
227
  },
209
- body: JSON.stringify(options),
210
228
  })
211
229
  if (valueType == "binary") {
212
230
  return handleServerResponse(response, {
@@ -237,16 +255,22 @@ class DiskSortedHashTableClient {
237
255
  const { valueType } = options
238
256
  const response = await this.http.POST(`/${this.tableName}/forwardEntriesIterator`, {
239
257
  headers: {
240
- "Content-Type": "application/json",
258
+ "Content-Type": "text/plain",
241
259
  "Client-Name": "DiskSortedHashTableClient",
242
- ...this.sortValueType ? {
243
- "Sort-Value-Type": this.sortValueType,
244
- } : {},
245
- ...this.degree ? {
246
- "Degree": Number(this.degree),
247
- } : {},
260
+
261
+ ...pipe(options, all({
262
+ "Exclusive-Start-Key": get("exclusiveStartKey"),
263
+ "Starting-Sort-Value": get("startingSortValue"),
264
+ "Ending-Sort-Value": get("endingSortValue"),
265
+ "Value-Type": get("valueType"),
266
+ }), filter(__), map(parseString)),
267
+
268
+ ...pipe(this, all({
269
+ "Sort-Value-Type": get("sortValueType"),
270
+ "Degree": get("degree"),
271
+ }), filter(__), map(parseString)),
272
+
248
273
  },
249
- body: JSON.stringify(options),
250
274
  })
251
275
  if (valueType == "binary") {
252
276
  const iter = await handleServerResponse(response, {
@@ -278,16 +302,22 @@ class DiskSortedHashTableClient {
278
302
  const { valueType } = options
279
303
  const response = await this.http.POST(`/${this.tableName}/reverseEntriesIterator`, {
280
304
  headers: {
281
- "Content-Type": "application/json",
305
+ "Content-Type": "text/plain",
282
306
  "Client-Name": "DiskSortedHashTableClient",
283
- ...this.sortValueType ? {
284
- "Sort-Value-Type": this.sortValueType,
285
- } : {},
286
- ...this.degree ? {
287
- "Degree": Number(this.degree),
288
- } : {},
307
+
308
+ ...pipe(options, all({
309
+ "Exclusive-Start-Key": get("exclusiveStartKey"),
310
+ "Starting-Sort-Value": get("startingSortValue"),
311
+ "Ending-Sort-Value": get("endingSortValue"),
312
+ "Value-Type": get("valueType"),
313
+ }), filter(__), map(parseString)),
314
+
315
+ ...pipe(this, all({
316
+ "Sort-Value-Type": get("sortValueType"),
317
+ "Degree": get("degree"),
318
+ }), filter(__), map(parseString)),
319
+
289
320
  },
290
- body: JSON.stringify(options),
291
321
  })
292
322
 
293
323
  if (valueType == "binary") {
@@ -319,16 +349,16 @@ class DiskSortedHashTableClient {
319
349
  async delete(key) {
320
350
  const response = await this.http.POST(`/${this.tableName}/delete`, {
321
351
  headers: {
322
- "Content-Type": "application/json",
352
+ "Content-Type": "text/plain",
323
353
  "Client-Name": "DiskSortedHashTableClient",
324
- ...this.sortValueType ? {
325
- "Sort-Value-Type": this.sortValueType,
326
- } : {},
327
- ...this.degree ? {
328
- "Degree": Number(this.degree),
329
- } : {},
354
+ "Key": key,
355
+
356
+ ...pipe(this, all({
357
+ "Sort-Value-Type": get("sortValueType"),
358
+ "Degree": get("degree"),
359
+ }), filter(__), map(parseString)),
360
+
330
361
  },
331
- body: JSON.stringify({ key }),
332
362
  })
333
363
  const didDeleteText = await handleServerResponse(response)
334
364
  return didDeleteText == "true"
@@ -345,13 +375,14 @@ class DiskSortedHashTableClient {
345
375
  async count() {
346
376
  const response = await this.http.POST(`/${this.tableName}/count`, {
347
377
  headers: {
378
+ "Content-Type": "text/plain",
348
379
  "Client-Name": "DiskSortedHashTableClient",
349
- ...this.sortValueType ? {
350
- "Sort-Value-Type": this.sortValueType,
351
- } : {},
352
- ...this.degree ? {
353
- "Degree": Number(this.degree),
354
- } : {},
380
+
381
+ ...pipe(this, all({
382
+ "Sort-Value-Type": get("sortValueType"),
383
+ "Degree": get("degree"),
384
+ }), filter(__), map(parseString)),
385
+
355
386
  },
356
387
  })
357
388
  return Number(await handleServerResponse(response))
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @name errorHandler
3
+ *
4
+ * @docs
5
+ * ```specscript
6
+ * module HTTP "https://presidium.services/docs/HTTP"
7
+ *
8
+ * errorHandler(error Error, request HTTP.Request, response HTTP.Response) -> ()
9
+ * ```
10
+ */
11
+ function errorHandler(error, request, response) {
12
+ response.writeHead(error.code ?? 500, {
13
+ "Content-Type": "text/plain",
14
+ })
15
+ response.end(error.message)
16
+ }
17
+
18
+ module.exports = errorHandler
@@ -0,0 +1,40 @@
1
+ const HTTP = require("presidium/HTTP")
2
+
3
+ /**
4
+ * @name loadBalancerHandler
5
+ *
6
+ * @docs
7
+ * ```specscript
8
+ * module HTTP "https://presidium.services/docs/HTTP"
9
+ *
10
+ * loadBalancerHandler(HTTP.Request, HTTP.Response) -> ()
11
+ * ```
12
+ */
13
+ async function loadBalancerHandler(request, response) {
14
+ const tableName = request.pathSegments[0]
15
+ const key = request.headers["key"]
16
+
17
+ let index = this.hash(tableName, key)
18
+ if (typeof index.then == "function") {
19
+ index = await index
20
+ }
21
+
22
+ const serverIp = this.serverIps[index]
23
+ let client
24
+ if (this.clients.has(serverIp)) {
25
+ client = this.clients.get(serverIp)
26
+ } else {
27
+ client = new HTTP(serverIp)
28
+ this.clients.set(serverIp, client)
29
+ }
30
+
31
+ const serverResponse = await client[request.method](request.url, {
32
+ headers: request.headers,
33
+ body: request,
34
+ })
35
+
36
+ response.writeHead(serverResponse.status, serverResponse.headers)
37
+ serverResponse.pipe(response)
38
+ }
39
+
40
+ module.exports = loadBalancerHandler
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "presidium-db-client-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Presidium DB client and server library",
5
5
  "author": "Richard Tong",
6
6
  "license": "CFOSS",
@@ -14,8 +14,10 @@
14
14
  "AsyncIterator.js",
15
15
  "BinaryEntriesStream.js",
16
16
  "BinaryStream.js",
17
- "DatabaseHandler.js",
18
17
  "DatabaseServer.js",
18
+ "DatabaseHandler.js",
19
+ "DatabaseLoadBalancer.js",
20
+ "loadBalancerHandler.js",
19
21
  "DiskHashTableClient.js",
20
22
  "DiskSortedHashTableClient.js",
21
23
  "TextEntriesStream.js",
@@ -23,7 +25,9 @@
23
25
  "buffersToEntry.js",
24
26
  "handleServerResponse.js",
25
27
  "healthCheckHandler.js",
26
- "notFoundHandler.js"
28
+ "notFoundHandler.js",
29
+ "errorHandler.js",
30
+ "parseString.js"
27
31
  ],
28
32
  "keywords": [
29
33
  "presidium",
@@ -41,7 +45,7 @@
41
45
  },
42
46
  "dependencies": {
43
47
  "presidium": "^4.5.0",
44
- "rubico": "^2.20.0"
48
+ "rubico": "^2.21.0"
45
49
  },
46
50
  "devDependencies": {
47
51
  "codecov": "^3.8.2"
package/parseString.js ADDED
@@ -0,0 +1,18 @@
1
+ const numberRegex = /^[0-9\.]+$/
2
+
3
+ /**
4
+ * @name parseString
5
+ *
6
+ * @docs
7
+ * ```specscript
8
+ * parseString(s string) -> number|string
9
+ * ```
10
+ */
11
+ function parseString(s) {
12
+ if (numberRegex.test(s)) {
13
+ return Number(s)
14
+ }
15
+ return s
16
+ }
17
+
18
+ module.exports = parseString