feat: mutex processing in a chat to preserve order of events

This commit is contained in:
Adhiraj Singh
2022-01-22 14:07:06 +05:30
parent a06f639774
commit 1f2a6641f3
4 changed files with 182 additions and 141 deletions

View File

@@ -7,7 +7,7 @@ import { decryptGroupSignalProto, decryptSignalProto, processSenderKeyMessage }
type MessageType = 'chat' | 'peer_broadcast' | 'other_broadcast' | 'group' | 'direct_peer_status' | 'other_status'
export const decodeMessageStanza = async(stanza: BinaryNode, auth: AuthenticationState) => {
export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationState) => {
//const deviceIdentity = (stanza.content as BinaryNodeM[])?.find(m => m.tag === 'device-identity')
//const deviceIdentityBytes = deviceIdentity ? deviceIdentity.content as Buffer : undefined
@@ -81,48 +81,51 @@ export const decodeMessageStanza = async(stanza: BinaryNode, auth: Authenticatio
fullMessage.status = proto.WebMessageInfo.WebMessageInfoStatus.SERVER_ACK
}
if(Array.isArray(stanza.content)) {
for(const { tag, attrs, content } of stanza.content) {
if(tag !== 'enc') {
continue
return {
fullMessage,
decryptionTask: (async() => {
if(Array.isArray(stanza.content)) {
for(const { tag, attrs, content } of stanza.content) {
if(tag !== 'enc') {
continue
}
if(!(content instanceof Uint8Array)) {
continue
}
let msgBuffer: Buffer
try {
const e2eType = attrs.type
switch (e2eType) {
case 'skmsg':
msgBuffer = await decryptGroupSignalProto(sender, author, content, auth)
break
case 'pkmsg':
case 'msg':
const user = isJidUser(sender) ? sender : author
msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth)
break
}
let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer))
msg = msg.deviceSentMessage?.message || msg
if(msg.senderKeyDistributionMessage) {
await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth)
}
if(fullMessage.message) {
Object.assign(fullMessage.message, msg)
} else {
fullMessage.message = msg
}
} catch(error) {
fullMessage.messageStubType = proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT
fullMessage.messageStubParameters = [error.message]
}
}
}
if(!(content instanceof Uint8Array)) {
continue
}
let msgBuffer: Buffer
try {
const e2eType = attrs.type
switch (e2eType) {
case 'skmsg':
msgBuffer = await decryptGroupSignalProto(sender, author, content, auth)
break
case 'pkmsg':
case 'msg':
const user = isJidUser(sender) ? sender : author
msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth)
break
}
let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer))
msg = msg.deviceSentMessage?.message || msg
if(msg.senderKeyDistributionMessage) {
await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth)
}
if(fullMessage.message) {
Object.assign(fullMessage.message, msg)
} else {
fullMessage.message = msg
}
} catch(error) {
fullMessage.messageStubType = proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT
fullMessage.messageStubParameters = [error.message]
}
}
})()
}
return fullMessage
}

View File

@@ -1,22 +1,36 @@
export default () => {
export const makeMutex = () => {
let task = Promise.resolve() as Promise<any>
return {
mutex<T>(code: () => Promise<T>):Promise<T> {
mutex<T>(code: () => Promise<T> | T): Promise<T> {
task = (async() => {
// wait for the previous task to complete
// if there is an error, we swallow so as to not block the queue
try {
await task
// wait for the previous task to complete
// if there is an error, we swallow so as to not block the queue
try {
await task
} catch{ }
// execute the current task
return code()
// execute the current task
return code()
})()
// we replace the existing task, appending the new piece of execution to it
// so the next task will have to wait for this one to finish
return task
},
},
}
}
export type Mutex = ReturnType<typeof makeMutex>
export const makeKeyedMutex = () => {
const map: { [id: string]: Mutex } = {}
return {
mutex<T>(key: string, task: () => Promise<T> | T): Promise<T> {
if(!map[key]) {
map[key] = makeMutex()
}
return map[key].mutex(task)
}
}
}