pairnet 0.1.1 → 0.2.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/lib/protocol.js DELETED
@@ -1,191 +0,0 @@
1
- "use strict";
2
-
3
- const encodeCBOR = require("@promistream/encode-cbor");
4
- const decodeCBOR = require("@promistream/decode-cbor");
5
- const pipe = require("@promistream/pipe");
6
- const fromNodeStream = require("@promistream/from-node-stream");
7
- const map = require("@promistream/map");
8
- const simpleQueue = require("@promistream/simple-queue");
9
- const simpleSink = require("@promistream/simple-sink");
10
- const createDebugStream = require("@promistream/debug");
11
- const objid = require("@joepie91/objid");
12
-
13
- const createSender = require("./sender");
14
- const createReplyTracker = require("./reply-tracker");
15
- const filter = require("@promistream/filter");
16
-
17
- let NORMAL_QUERY_TYPE = "q";
18
- let NEGOTIATION_QUERY_TYPE = "nq";
19
- let EVENT_TYPE = "e";
20
- // negotiation query, negotiation reply, negotiation reply error
21
- let NEGOTIATION_TYPES = new Set([ "nq", "n", "N" ]);
22
- // reply, reply error, negotiation reply, negotiation reply error
23
- let REPLY_TYPES = new Set([ "r", "R", "n", "N" ]);
24
- let SUCCESS_REPLIES = new Set([ "r", "n" ]);
25
- let ERROR_REPLIES = new Set([ "R", "N" ]);
26
-
27
- async function evaluateQuery(query, handler) {
28
- try {
29
- return {
30
- success: true,
31
- message: await handler(query)
32
- };
33
- } catch (error) {
34
- if (error.code === "QUERY_ERROR_RESPONSE") {
35
- return {
36
- success: false,
37
- message: error.messageData
38
- };
39
- } else {
40
- throw error;
41
- }
42
- }
43
- }
44
-
45
- module.exports = async function handleProtocol(socket, { debug, onQuery, protocolConfiguration, role } = {}) {
46
- debug(`Attaching semantic protocol handler (role = ${role})`);
47
-
48
- let writeQueue = simpleQueue([], { autoEnd: false });
49
- let replyTracker = createReplyTracker();
50
- let negotiationCompleted = false;
51
-
52
- let sender = createSender(replyTracker, (message) => {
53
- writeQueue.push(message);
54
- });
55
-
56
- // Send pipeline
57
- pipe([
58
- writeQueue.stream,
59
- map((message) => {
60
- debug("Sending message:", message);
61
-
62
- let isNegotiationMessage = NEGOTIATION_TYPES.has(message.t);
63
-
64
- if (negotiationCompleted !== true && !isNegotiationMessage) {
65
- debug(`[handleProtocol] Prevented message send; not allowed type '${message.t}' during negotiation`);
66
- throw new Error(`Cannot send messages in negotiation phase`);
67
- } else {
68
- return message;
69
- }
70
- }),
71
- // createDebugStream(`pairnet:protocol:send-pipeline ${objid(socket)}`),
72
- encodeCBOR(),
73
- fromNodeStream.fromWritable(socket)
74
- ]).read().catch((error) => {
75
- // These errors are also mirrored on the read side, so we don't need to handle them here
76
- debug(`Error occurred on writable side of socket`, error);
77
- });
78
-
79
- let incomingMessageStream = pipe([
80
- fromNodeStream.fromReadable(socket),
81
- decodeCBOR(),
82
- // createDebugStream(`pairnet:protocol:receive-pipeline ${objid(socket)}`),
83
- ]);
84
-
85
- let clientHelloQuery = (role === "client")
86
- ? sender.sendNegotiationQuery(await protocolConfiguration.generateClientHello())._queryID.toString("hex") // FIXME: Hacky
87
- : null;
88
-
89
- // We read messages for as long as negotiation has not been completed yet
90
- // TODO[security]: verify that we don't allow server/client confusion anywhere
91
- await pipe([
92
- incomingMessageStream,
93
- simpleSink(async (message) => {
94
- debug("Received message (negotiation phase):", message);
95
-
96
- let isNegotiationMessage = NEGOTIATION_TYPES.has(message.t);
97
- let isReplyMessage = REPLY_TYPES.has(message.t);
98
-
99
- if (!isNegotiationMessage) {
100
- debug(`[readLoop] Ignored received message; not allowed type '${message.t}' during negotiation`);
101
- } else if (isReplyMessage) {
102
- if (message.i.toString("hex") === clientHelloQuery) {
103
- let helloResponse = message.m;
104
- let helloDecision = await protocolConfiguration.onServerHello(helloResponse);
105
-
106
- if (helloDecision === false) {
107
- // Connection rejected
108
- return writeQueue.end();
109
- } else {
110
- debug(`[readLoop] Negotiation completed on client side; detaching sink...`);
111
- return null;
112
- }
113
- } else {
114
- throw new Error(`Unrecognized negotiation query reply, id: ${message.i}`);
115
- }
116
- } else if (message.t === NEGOTIATION_QUERY_TYPE) {
117
- let result = await evaluateQuery(message.m, protocolConfiguration.onClientHello);
118
-
119
- if (result.success) {
120
- await sender.sendNegotiationReply(message.i, result.message);
121
-
122
- debug(`[readLoop] Negotiation completed on server side; detaching sink...`);
123
- return null;
124
- } else {
125
- await sender.sendNegotiationReplyError(message.i, result.message);
126
- }
127
-
128
- debug(`[readLoop] Processed negotiation query ID '${message.i.toString("hex")}'`);
129
- } else {
130
- debug(`[readLoop] Received message with unknown type '${message.t}'`);
131
- }
132
- })
133
- ]).read();
134
-
135
- // Unlock the sender
136
- negotiationCompleted = true;
137
-
138
- let eventStream = pipe([
139
- incomingMessageStream,
140
- filter(async (message) => {
141
- debug("Received message (operating phase):", message);
142
-
143
- let isReplyMessage = REPLY_TYPES.has(message.t);
144
-
145
- if (message.t === NORMAL_QUERY_TYPE) {
146
- let result = await evaluateQuery(message.m, onQuery);
147
-
148
- if (result.success) {
149
- await sender.sendReply(message.i, result.message);
150
- } else {
151
- await sender.sendReplyError(message.i, result.message);
152
- }
153
-
154
- debug(`[readLoop] Processed normal query ID '${message.i.toString("hex")}'`);
155
- return false;
156
- } else if (message.t === EVENT_TYPE) {
157
- // This is the only type of message that we will pass through as-is
158
- debug(`[readLoop] Passed through event message as-is`);
159
- return true;
160
- } else if (isReplyMessage) {
161
- try {
162
- if (SUCCESS_REPLIES.has(message.t)) {
163
- replyTracker.registerReply(message.i).resolve(message.m);
164
- } else if (ERROR_REPLIES.has(message.t)) {
165
- let error = new Error(`Received error response from peer`);
166
- error.code = "PEER_ERROR";
167
- error.message = message.m;
168
-
169
- replyTracker.registerReply(message.i).reject(error);
170
- }
171
- } catch (error) {
172
- if (error.code === "UNKNOWN_QUERY_ID") {
173
- debug(`[readLoop] Received reply to unknown query ID '${message.i.toString("hex")}'`);
174
- }
175
- }
176
- } else {
177
- debug(`[readLoop] Received message with unknown type '${message.t}'`);
178
- return false;
179
- }
180
- }),
181
- map(async (message) => {
182
- return message.m; // Unpack actual message content from event message
183
- })
184
- ]);
185
-
186
- return {
187
- eventStream: eventStream,
188
- sender: sender,
189
- disconnect: () => writeQueue.end()
190
- };
191
- };
File without changes
File without changes
File without changes