presidium-db-client-server 0.7.2 → 4.1.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.
- package/DatabaseServer.js +2 -14
- package/databaseHandler.js +316 -0
- package/loadBalancerHandler.js +18 -14
- package/package.json +2 -2
- package/DatabaseHandler.js +0 -330
package/DatabaseServer.js
CHANGED
|
@@ -6,7 +6,7 @@ require("rubico/global")
|
|
|
6
6
|
const HTTP = require("presidium/HTTP")
|
|
7
7
|
const DiskHashTable = require("presidium/DiskHashTable")
|
|
8
8
|
const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
9
|
-
const
|
|
9
|
+
const databaseHandler = require("./databaseHandler")
|
|
10
10
|
const healthCheckHandler = require("./healthCheckHandler")
|
|
11
11
|
const notFoundHandler = require("./notFoundHandler")
|
|
12
12
|
const errorHandler = require("./errorHandler")
|
|
@@ -63,25 +63,13 @@ class DatabaseServer {
|
|
|
63
63
|
await manifestHt.init()
|
|
64
64
|
this.manifestHt = manifestHt
|
|
65
65
|
|
|
66
|
-
const databaseHandler = DatabaseHandler({
|
|
67
|
-
databasePath: this.databasePath,
|
|
68
|
-
manifestTableName: this.manifestTableName,
|
|
69
|
-
manifestTableInitialLength: this.manifestTableInitialLength,
|
|
70
|
-
manifestTableItemSize: this.manifestTableItemSize,
|
|
71
|
-
defaultTableInitialLength: this.defaultTableInitialLength,
|
|
72
|
-
defaultTableItemSize: this.defaultTableItemSize,
|
|
73
|
-
defaultDiskSortedHashTableDegree: this.defaultDiskSortedHashTableDegree,
|
|
74
|
-
manifestHt: this.manifestHt,
|
|
75
|
-
hts: this.hts,
|
|
76
|
-
})
|
|
77
|
-
|
|
78
66
|
const router = tryCatch(
|
|
79
67
|
switchCase(
|
|
80
68
|
request => request.path == "/health",
|
|
81
69
|
healthCheckHandler,
|
|
82
70
|
|
|
83
71
|
request => request.pathDepth == 2,
|
|
84
|
-
databaseHandler,
|
|
72
|
+
curry.call(databaseHandler, this),
|
|
85
73
|
|
|
86
74
|
notFoundHandler,
|
|
87
75
|
),
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
require("rubico/global")
|
|
6
|
+
const DiskHashTable = require("presidium/DiskHashTable")
|
|
7
|
+
const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
8
|
+
const Readable = require("presidium/Readable")
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @name getHt
|
|
12
|
+
*
|
|
13
|
+
* @docs
|
|
14
|
+
* ```specscript
|
|
15
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
16
|
+
*
|
|
17
|
+
* getHt(tableName string, request HTTP.Request) -> ht DiskHashTable|DiskSortedHashTable
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
async function getHt(tableName, request) {
|
|
21
|
+
const {
|
|
22
|
+
databasePath,
|
|
23
|
+
defaultTableInitialLength,
|
|
24
|
+
defaultTableItemSize,
|
|
25
|
+
manifestHt,
|
|
26
|
+
hts,
|
|
27
|
+
} = this
|
|
28
|
+
|
|
29
|
+
const htConfigString = await manifestHt.get(tableName)
|
|
30
|
+
let ht
|
|
31
|
+
if (htConfigString) {
|
|
32
|
+
const config = JSON.parse(htConfigString)
|
|
33
|
+
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
34
|
+
ht = new DiskHashTable(config)
|
|
35
|
+
await ht.init()
|
|
36
|
+
hts.set(tableName, ht)
|
|
37
|
+
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
38
|
+
ht = new DiskSortedHashTable(config)
|
|
39
|
+
await ht.init()
|
|
40
|
+
hts.set(tableName, ht)
|
|
41
|
+
} else {
|
|
42
|
+
const error = new Error("Forbidden: Client-Name")
|
|
43
|
+
error.code = 403
|
|
44
|
+
throw error
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
48
|
+
const config = {
|
|
49
|
+
storagePath: `${databasePath}/${tableName}/storage`,
|
|
50
|
+
headerPath: `${databasePath}/${tableName}/header`,
|
|
51
|
+
initialLength: defaultTableInitialLength,
|
|
52
|
+
itemSize: defaultTableItemSize,
|
|
53
|
+
}
|
|
54
|
+
ht = new DiskHashTable(config)
|
|
55
|
+
await manifestHt.set(tableName, JSON.stringify(config))
|
|
56
|
+
await ht.init()
|
|
57
|
+
hts.set(tableName, ht)
|
|
58
|
+
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
59
|
+
const config = {
|
|
60
|
+
storagePath: `${databasePath}/${tableName}/storage`,
|
|
61
|
+
headerPath: `${databasePath}/${tableName}/header`,
|
|
62
|
+
initialLength: defaultTableInitialLength,
|
|
63
|
+
itemSize: defaultTableItemSize,
|
|
64
|
+
sortValueType: request.headers["sort-value-type"],
|
|
65
|
+
degree: Number(request.headers["degree"]),
|
|
66
|
+
}
|
|
67
|
+
ht = new DiskSortedHashTable(config)
|
|
68
|
+
await manifestHt.set(tableName, JSON.stringify(config))
|
|
69
|
+
await ht.init()
|
|
70
|
+
hts.set(tableName, ht)
|
|
71
|
+
} else {
|
|
72
|
+
const error = new Error("Forbidden: Client-Name")
|
|
73
|
+
error.code = 403
|
|
74
|
+
throw error
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return ht
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @name databaseHandler
|
|
82
|
+
*
|
|
83
|
+
* @docs
|
|
84
|
+
* ```specscript
|
|
85
|
+
* module DiskHashTable "https://presidium.services/docs/DiskHashTable"
|
|
86
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
87
|
+
*
|
|
88
|
+
* databaseHandler(HTTP.Request, HTTP.Response) -> ()
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
async function databaseHandler(request, response) {
|
|
92
|
+
const { pathname } = new URL(`http://*${request.url}`)
|
|
93
|
+
request.path = decodeURIComponent(
|
|
94
|
+
pathname == "/" ? pathname
|
|
95
|
+
: pathname.endsWith("/") ? pathname.slice(0, -1)
|
|
96
|
+
: pathname
|
|
97
|
+
)
|
|
98
|
+
request.pathSegments = request.path.slice(1).split("/")
|
|
99
|
+
request.pathDepth = request.pathSegments.length
|
|
100
|
+
|
|
101
|
+
const tableName = request.pathSegments[0]
|
|
102
|
+
const fname = request.pathSegments[1]
|
|
103
|
+
const ht = this.hts.get(tableName) ?? await getHt.call(this, tableName, request)
|
|
104
|
+
|
|
105
|
+
await switchCase(
|
|
106
|
+
fname,
|
|
107
|
+
|
|
108
|
+
eq("clear"), async () => {
|
|
109
|
+
await ht.clear()
|
|
110
|
+
response.writeHead(200, {
|
|
111
|
+
"Content-Type": "text/plain",
|
|
112
|
+
})
|
|
113
|
+
response.end("OK")
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
eq("set"), async () => {
|
|
117
|
+
const key = request.headers["key"]
|
|
118
|
+
const sortValue = request.headers["sort-value"]
|
|
119
|
+
const value = request.headers["content-type"] == "text/plain"
|
|
120
|
+
? await Readable.Text(request)
|
|
121
|
+
: await Readable.Buffer(request)
|
|
122
|
+
await ht.set(key, value, sortValue)
|
|
123
|
+
response.writeHead(200, {
|
|
124
|
+
"Content-Type": "text/plain",
|
|
125
|
+
})
|
|
126
|
+
response.end("OK")
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
eq("get"), async () => {
|
|
130
|
+
const key = request.headers["key"]
|
|
131
|
+
const value = await ht.get(key)
|
|
132
|
+
if (value === undefined) {
|
|
133
|
+
response.writeHead(499, {
|
|
134
|
+
"Content-Type": "text/plain",
|
|
135
|
+
})
|
|
136
|
+
response.end("Resource Not Found")
|
|
137
|
+
} else {
|
|
138
|
+
response.writeHead(200, {
|
|
139
|
+
"Content-Type": "text/plain",
|
|
140
|
+
})
|
|
141
|
+
response.end(value)
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
eq("getBinary"), async () => {
|
|
146
|
+
const key = request.headers["key"]
|
|
147
|
+
const value = await ht.getBinary(key)
|
|
148
|
+
response.writeHead(200, {
|
|
149
|
+
"Content-Type": "application/octet-stream",
|
|
150
|
+
})
|
|
151
|
+
response.end(value)
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
eq("delete"), async () => {
|
|
155
|
+
const key = request.headers["key"]
|
|
156
|
+
const didDelete = await ht.delete(key)
|
|
157
|
+
response.writeHead(200, {
|
|
158
|
+
"Content-Type": "text/plain",
|
|
159
|
+
})
|
|
160
|
+
response.end(String(didDelete))
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
eq("count"), async () => {
|
|
164
|
+
const count = ht.count()
|
|
165
|
+
response.writeHead(200, {
|
|
166
|
+
"Content-Type": "text/plain",
|
|
167
|
+
})
|
|
168
|
+
response.end(String(count))
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
eq("iterator"), async () => {
|
|
172
|
+
const valueType = request.headers["value-type"]
|
|
173
|
+
const options = filter({ valueType }, __)
|
|
174
|
+
response.writeHead(200, {
|
|
175
|
+
"Content-Type": "application/octet-stream",
|
|
176
|
+
"Content-Encoding": "utf8",
|
|
177
|
+
})
|
|
178
|
+
const count = ht.count()
|
|
179
|
+
for await (const value of ht.iterator(options)) {
|
|
180
|
+
response.write(value)
|
|
181
|
+
response.write("")
|
|
182
|
+
}
|
|
183
|
+
response.end()
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
eq("entriesIterator"), async () => {
|
|
187
|
+
const valueType = request.headers["value-type"]
|
|
188
|
+
const options = filter({ valueType }, __)
|
|
189
|
+
response.writeHead(200, {
|
|
190
|
+
"Content-Type": "application/octet-stream",
|
|
191
|
+
"Content-Encoding": "utf8",
|
|
192
|
+
})
|
|
193
|
+
const count = ht.count()
|
|
194
|
+
for await (const [key, value] of ht.entriesIterator(options)) {
|
|
195
|
+
response.write(key)
|
|
196
|
+
response.write("")
|
|
197
|
+
response.write(value)
|
|
198
|
+
response.write("")
|
|
199
|
+
}
|
|
200
|
+
response.end()
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
eq("forwardIterator"), async () => {
|
|
204
|
+
const valueType = request.headers["value-type"]
|
|
205
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
206
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
207
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
208
|
+
const options = filter({
|
|
209
|
+
valueType,
|
|
210
|
+
exclusiveStartKey,
|
|
211
|
+
startingSortValue,
|
|
212
|
+
endingSortValue,
|
|
213
|
+
}, __)
|
|
214
|
+
|
|
215
|
+
response.writeHead(200, {
|
|
216
|
+
"Content-Type": "application/octet-stream",
|
|
217
|
+
"Content-Encoding": "utf8",
|
|
218
|
+
})
|
|
219
|
+
const count = ht.count()
|
|
220
|
+
for await (const value of ht.forwardIterator(options)) {
|
|
221
|
+
response.write(value)
|
|
222
|
+
response.write("")
|
|
223
|
+
}
|
|
224
|
+
response.end()
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
eq("reverseIterator"), async () => {
|
|
228
|
+
const valueType = request.headers["value-type"]
|
|
229
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
230
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
231
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
232
|
+
const options = filter({
|
|
233
|
+
valueType,
|
|
234
|
+
exclusiveStartKey,
|
|
235
|
+
startingSortValue,
|
|
236
|
+
endingSortValue,
|
|
237
|
+
}, __)
|
|
238
|
+
|
|
239
|
+
response.writeHead(200, {
|
|
240
|
+
"Content-Type": "application/octet-stream",
|
|
241
|
+
"Content-Encoding": "utf8",
|
|
242
|
+
})
|
|
243
|
+
const count = ht.count()
|
|
244
|
+
for await (const value of ht.reverseIterator(options)) {
|
|
245
|
+
response.write(value)
|
|
246
|
+
response.write("")
|
|
247
|
+
}
|
|
248
|
+
response.end()
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
eq("forwardEntriesIterator"), async () => {
|
|
252
|
+
const valueType = request.headers["value-type"]
|
|
253
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
254
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
255
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
256
|
+
const options = filter({
|
|
257
|
+
valueType,
|
|
258
|
+
exclusiveStartKey,
|
|
259
|
+
startingSortValue,
|
|
260
|
+
endingSortValue,
|
|
261
|
+
}, __)
|
|
262
|
+
|
|
263
|
+
response.writeHead(200, {
|
|
264
|
+
"Content-Type": "application/octet-stream",
|
|
265
|
+
"Content-Encoding": "utf8",
|
|
266
|
+
})
|
|
267
|
+
const count = ht.count()
|
|
268
|
+
for await (const [key, value, sortValue] of ht.forwardEntriesIterator(options)) {
|
|
269
|
+
response.write(key)
|
|
270
|
+
response.write("")
|
|
271
|
+
response.write(value)
|
|
272
|
+
response.write("")
|
|
273
|
+
response.write(String(sortValue))
|
|
274
|
+
response.write("")
|
|
275
|
+
}
|
|
276
|
+
response.end()
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
eq("reverseEntriesIterator"), async () => {
|
|
280
|
+
const valueType = request.headers["value-type"]
|
|
281
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
282
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
283
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
284
|
+
const options = filter({
|
|
285
|
+
valueType,
|
|
286
|
+
exclusiveStartKey,
|
|
287
|
+
startingSortValue,
|
|
288
|
+
endingSortValue,
|
|
289
|
+
}, __)
|
|
290
|
+
|
|
291
|
+
response.writeHead(200, {
|
|
292
|
+
"Content-Type": "application/octet-stream",
|
|
293
|
+
"Content-Encoding": "utf8",
|
|
294
|
+
})
|
|
295
|
+
const count = ht.count()
|
|
296
|
+
for await (const [key, value, sortValue] of ht.reverseEntriesIterator(options)) {
|
|
297
|
+
response.write(key)
|
|
298
|
+
response.write("")
|
|
299
|
+
response.write(value)
|
|
300
|
+
response.write("")
|
|
301
|
+
response.write(String(sortValue))
|
|
302
|
+
response.write("")
|
|
303
|
+
}
|
|
304
|
+
response.end()
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
async () => {
|
|
308
|
+
const error = new Error("Forbidden: Hash Table Method")
|
|
309
|
+
error.code = "403"
|
|
310
|
+
throw error
|
|
311
|
+
},
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
module.exports = databaseHandler
|
package/loadBalancerHandler.js
CHANGED
|
@@ -50,7 +50,19 @@ async function loadBalancerHandler(request, response) {
|
|
|
50
50
|
passthroughStreams.push(new stream.PassThrough())
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
request.on("data", chunk => {
|
|
54
|
+
passthroughStreams.forEach(stream => {
|
|
55
|
+
stream.write(chunk)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
request.on("end", () => {
|
|
60
|
+
passthroughStreams.forEach(stream => {
|
|
61
|
+
stream.end()
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
await forEach(serverIpOrIps, async (serverIp, index) => {
|
|
54
66
|
const client = getClient.call(this, serverIp)
|
|
55
67
|
|
|
56
68
|
const serverResponse = await client[request.method](request.url, {
|
|
@@ -62,22 +74,14 @@ async function loadBalancerHandler(request, response) {
|
|
|
62
74
|
response.writeHead(serverResponse.status, serverResponse.headers)
|
|
63
75
|
serverResponse.pipe(response)
|
|
64
76
|
} else {
|
|
65
|
-
await Readable.Text(serverResponse)
|
|
77
|
+
const text = await Readable.Text(serverResponse)
|
|
78
|
+
if (serverResponse.ok) {
|
|
79
|
+
} else {
|
|
80
|
+
console.error(new Error(text))
|
|
81
|
+
}
|
|
66
82
|
}
|
|
67
83
|
})
|
|
68
84
|
|
|
69
|
-
request.on("data", chunk => {
|
|
70
|
-
passthroughStreams.forEach(stream => {
|
|
71
|
-
stream.write(chunk)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
request.on("end", () => {
|
|
76
|
-
passthroughStreams.forEach(stream => {
|
|
77
|
-
stream.end()
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
85
|
} else {
|
|
82
86
|
const client = getClient.call(this, serverIpOrIps)
|
|
83
87
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "presidium-db-client-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Presidium DB client and server library",
|
|
5
5
|
"author": "Richard Tong",
|
|
6
6
|
"license": "CFOSS",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"BinaryEntriesStream.js",
|
|
16
16
|
"BinaryStream.js",
|
|
17
17
|
"DatabaseServer.js",
|
|
18
|
-
"
|
|
18
|
+
"databaseHandler.js",
|
|
19
19
|
"DatabaseLoadBalancer.js",
|
|
20
20
|
"loadBalancerHandler.js",
|
|
21
21
|
"DiskHashTableClient.js",
|
package/DatabaseHandler.js
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
require("rubico/global")
|
|
6
|
-
const DiskHashTable = require("presidium/DiskHashTable")
|
|
7
|
-
const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
8
|
-
const Readable = require("presidium/Readable")
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @name DatabaseHandler
|
|
12
|
-
*
|
|
13
|
-
* @docs
|
|
14
|
-
* ```specscript
|
|
15
|
-
* module DiskHashTable "https://presidium.services/docs/DiskHashTable"
|
|
16
|
-
* module HTTP "https://presidium.services/docs/HTTP"
|
|
17
|
-
*
|
|
18
|
-
* DatabaseHandler(options {
|
|
19
|
-
* databasePath: string,
|
|
20
|
-
* manifestTableName: string,
|
|
21
|
-
* manifestTableInitialLength: number,
|
|
22
|
-
* manifestTableItemSize: number,
|
|
23
|
-
* defaultTableInitialLength: number,
|
|
24
|
-
* defaultTableItemSize: number,
|
|
25
|
-
* manifestHt: DiskHashTable,
|
|
26
|
-
* hts: Map<tableName string, DiskHashTable>,
|
|
27
|
-
* }) -> databaseHandler HTTP.AsyncHandler
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
function DatabaseHandler(options) {
|
|
31
|
-
const {
|
|
32
|
-
databasePath,
|
|
33
|
-
manifestTableName,
|
|
34
|
-
manifestTableInitialLength,
|
|
35
|
-
manifestTableItemSize,
|
|
36
|
-
defaultTableInitialLength,
|
|
37
|
-
defaultTableItemSize,
|
|
38
|
-
manifestHt,
|
|
39
|
-
hts,
|
|
40
|
-
} = options
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @name getHt
|
|
44
|
-
*
|
|
45
|
-
* @docs
|
|
46
|
-
* ```specscript
|
|
47
|
-
* module HTTP "https://presidium.services/docs/HTTP"
|
|
48
|
-
*
|
|
49
|
-
* getHt(tableName string, request HTTP.Request) -> ht DiskHashTable|DiskSortedHashTable
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
async function getHt(tableName, request, manifestHt) {
|
|
53
|
-
const htConfigString = await manifestHt.get(tableName)
|
|
54
|
-
let ht
|
|
55
|
-
if (htConfigString) {
|
|
56
|
-
const config = JSON.parse(htConfigString)
|
|
57
|
-
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
58
|
-
ht = new DiskHashTable(config)
|
|
59
|
-
await ht.init()
|
|
60
|
-
hts.set(tableName, ht)
|
|
61
|
-
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
62
|
-
ht = new DiskSortedHashTable(config)
|
|
63
|
-
await ht.init()
|
|
64
|
-
hts.set(tableName, ht)
|
|
65
|
-
} else {
|
|
66
|
-
const error = new Error("Forbidden: Client-Name")
|
|
67
|
-
error.code = 403
|
|
68
|
-
throw error
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
72
|
-
const config = {
|
|
73
|
-
storagePath: `${databasePath}/${tableName}/storage`,
|
|
74
|
-
headerPath: `${databasePath}/${tableName}/header`,
|
|
75
|
-
initialLength: defaultTableInitialLength,
|
|
76
|
-
itemSize: defaultTableItemSize,
|
|
77
|
-
}
|
|
78
|
-
ht = new DiskHashTable(config)
|
|
79
|
-
await manifestHt.set(tableName, JSON.stringify(config))
|
|
80
|
-
await ht.init()
|
|
81
|
-
hts.set(tableName, ht)
|
|
82
|
-
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
83
|
-
const config = {
|
|
84
|
-
storagePath: `${databasePath}/${tableName}/storage`,
|
|
85
|
-
headerPath: `${databasePath}/${tableName}/header`,
|
|
86
|
-
initialLength: defaultTableInitialLength,
|
|
87
|
-
itemSize: defaultTableItemSize,
|
|
88
|
-
sortValueType: request.headers["sort-value-type"],
|
|
89
|
-
degree: Number(request.headers["degree"]),
|
|
90
|
-
}
|
|
91
|
-
ht = new DiskSortedHashTable(config)
|
|
92
|
-
await manifestHt.set(tableName, JSON.stringify(config))
|
|
93
|
-
await ht.init()
|
|
94
|
-
hts.set(tableName, ht)
|
|
95
|
-
} else {
|
|
96
|
-
const error = new Error("Forbidden: Client-Name")
|
|
97
|
-
error.code = 403
|
|
98
|
-
throw error
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return ht
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return async function databaseHandler(request, response) {
|
|
105
|
-
const { pathname } = new URL(`http://*${request.url}`)
|
|
106
|
-
request.path = decodeURIComponent(
|
|
107
|
-
pathname == "/" ? pathname
|
|
108
|
-
: pathname.endsWith("/") ? pathname.slice(0, -1)
|
|
109
|
-
: pathname
|
|
110
|
-
)
|
|
111
|
-
request.pathSegments = request.path.slice(1).split("/")
|
|
112
|
-
request.pathDepth = request.pathSegments.length
|
|
113
|
-
|
|
114
|
-
const tableName = request.pathSegments[0]
|
|
115
|
-
const fname = request.pathSegments[1]
|
|
116
|
-
const ht = hts.get(tableName) ?? await getHt(tableName, request, manifestHt)
|
|
117
|
-
|
|
118
|
-
await switchCase(
|
|
119
|
-
fname,
|
|
120
|
-
|
|
121
|
-
eq("clear"), async () => {
|
|
122
|
-
await ht.clear()
|
|
123
|
-
response.writeHead(200, {
|
|
124
|
-
"Content-Type": "text/plain",
|
|
125
|
-
})
|
|
126
|
-
response.end("OK")
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
eq("set"), async () => {
|
|
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)
|
|
135
|
-
await ht.set(key, value, sortValue)
|
|
136
|
-
response.writeHead(200, {
|
|
137
|
-
"Content-Type": "text/plain",
|
|
138
|
-
})
|
|
139
|
-
response.end("OK")
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
eq("get"), async () => {
|
|
143
|
-
const key = request.headers["key"]
|
|
144
|
-
const value = await ht.get(key)
|
|
145
|
-
if (value === undefined) {
|
|
146
|
-
response.writeHead(499, {
|
|
147
|
-
"Content-Type": "text/plain",
|
|
148
|
-
})
|
|
149
|
-
response.end("Resource Not Found")
|
|
150
|
-
} else {
|
|
151
|
-
response.writeHead(200, {
|
|
152
|
-
"Content-Type": "text/plain",
|
|
153
|
-
})
|
|
154
|
-
response.end(value)
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
eq("getBinary"), async () => {
|
|
159
|
-
const key = request.headers["key"]
|
|
160
|
-
const value = await ht.getBinary(key)
|
|
161
|
-
response.writeHead(200, {
|
|
162
|
-
"Content-Type": "application/octet-stream",
|
|
163
|
-
})
|
|
164
|
-
response.end(value)
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
eq("delete"), async () => {
|
|
168
|
-
const key = request.headers["key"]
|
|
169
|
-
const didDelete = await ht.delete(key)
|
|
170
|
-
response.writeHead(200, {
|
|
171
|
-
"Content-Type": "text/plain",
|
|
172
|
-
})
|
|
173
|
-
response.end(String(didDelete))
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
eq("count"), async () => {
|
|
177
|
-
const count = ht.count()
|
|
178
|
-
response.writeHead(200, {
|
|
179
|
-
"Content-Type": "text/plain",
|
|
180
|
-
})
|
|
181
|
-
response.end(String(count))
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
eq("iterator"), async () => {
|
|
185
|
-
const valueType = request.headers["value-type"]
|
|
186
|
-
const options = filter({ valueType }, __)
|
|
187
|
-
response.writeHead(200, {
|
|
188
|
-
"Content-Type": "application/octet-stream",
|
|
189
|
-
"Content-Encoding": "utf8",
|
|
190
|
-
})
|
|
191
|
-
const count = ht.count()
|
|
192
|
-
for await (const value of ht.iterator(options)) {
|
|
193
|
-
response.write(value)
|
|
194
|
-
response.write("")
|
|
195
|
-
}
|
|
196
|
-
response.end()
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
eq("entriesIterator"), async () => {
|
|
200
|
-
const valueType = request.headers["value-type"]
|
|
201
|
-
const options = filter({ valueType }, __)
|
|
202
|
-
response.writeHead(200, {
|
|
203
|
-
"Content-Type": "application/octet-stream",
|
|
204
|
-
"Content-Encoding": "utf8",
|
|
205
|
-
})
|
|
206
|
-
const count = ht.count()
|
|
207
|
-
for await (const [key, value] of ht.entriesIterator(options)) {
|
|
208
|
-
response.write(key)
|
|
209
|
-
response.write("")
|
|
210
|
-
response.write(value)
|
|
211
|
-
response.write("")
|
|
212
|
-
}
|
|
213
|
-
response.end()
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
eq("forwardIterator"), async () => {
|
|
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
|
-
|
|
228
|
-
response.writeHead(200, {
|
|
229
|
-
"Content-Type": "application/octet-stream",
|
|
230
|
-
"Content-Encoding": "utf8",
|
|
231
|
-
})
|
|
232
|
-
const count = ht.count()
|
|
233
|
-
for await (const value of ht.forwardIterator(options)) {
|
|
234
|
-
response.write(value)
|
|
235
|
-
response.write("")
|
|
236
|
-
}
|
|
237
|
-
response.end()
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
eq("reverseIterator"), async () => {
|
|
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
|
-
|
|
252
|
-
response.writeHead(200, {
|
|
253
|
-
"Content-Type": "application/octet-stream",
|
|
254
|
-
"Content-Encoding": "utf8",
|
|
255
|
-
})
|
|
256
|
-
const count = ht.count()
|
|
257
|
-
for await (const value of ht.reverseIterator(options)) {
|
|
258
|
-
response.write(value)
|
|
259
|
-
response.write("")
|
|
260
|
-
}
|
|
261
|
-
response.end()
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
eq("forwardEntriesIterator"), async () => {
|
|
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
|
-
|
|
276
|
-
response.writeHead(200, {
|
|
277
|
-
"Content-Type": "application/octet-stream",
|
|
278
|
-
"Content-Encoding": "utf8",
|
|
279
|
-
})
|
|
280
|
-
const count = ht.count()
|
|
281
|
-
for await (const [key, value, sortValue] of ht.forwardEntriesIterator(options)) {
|
|
282
|
-
response.write(key)
|
|
283
|
-
response.write("")
|
|
284
|
-
response.write(value)
|
|
285
|
-
response.write("")
|
|
286
|
-
response.write(String(sortValue))
|
|
287
|
-
response.write("")
|
|
288
|
-
}
|
|
289
|
-
response.end()
|
|
290
|
-
},
|
|
291
|
-
|
|
292
|
-
eq("reverseEntriesIterator"), async () => {
|
|
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
|
-
|
|
304
|
-
response.writeHead(200, {
|
|
305
|
-
"Content-Type": "application/octet-stream",
|
|
306
|
-
"Content-Encoding": "utf8",
|
|
307
|
-
})
|
|
308
|
-
const count = ht.count()
|
|
309
|
-
for await (const [key, value, sortValue] of ht.reverseEntriesIterator(options)) {
|
|
310
|
-
response.write(key)
|
|
311
|
-
response.write("")
|
|
312
|
-
response.write(value)
|
|
313
|
-
response.write("")
|
|
314
|
-
response.write(String(sortValue))
|
|
315
|
-
response.write("")
|
|
316
|
-
}
|
|
317
|
-
response.end()
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
async () => {
|
|
321
|
-
const error = new Error("Forbidden: Hash Table Method")
|
|
322
|
-
error.code = "403"
|
|
323
|
-
throw error
|
|
324
|
-
},
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
module.exports = DatabaseHandler
|