aboutsummaryrefslogtreecommitdiff
path: root/src/server/websocket.ts
diff options
context:
space:
mode:
authorZachary Zhang <zacharyzhang7@gmail.com>2024-08-31 00:46:29 -0400
committerZachary Zhang <zacharyzhang7@gmail.com>2024-08-31 00:46:29 -0400
commit196294f331496262bef256da8b8e9dbc80288bea (patch)
tree85ff27b7a8070585f9a5ef71dff63566e03232ba /src/server/websocket.ts
parent0cf61501ec9be34294935f01973c1bd9cad6d267 (diff)
parentc36607691e0b7f5c04f3209a64958f5e51ddd785 (diff)
Merge branch 'master' into zach-starter
Diffstat (limited to 'src/server/websocket.ts')
-rw-r--r--src/server/websocket.ts378
1 files changed, 99 insertions, 279 deletions
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index cece8a1b7..1e25a8a27 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -3,66 +3,32 @@ import { createServer } from 'https';
import * as _ from 'lodash';
import { networkInterfaces } from 'os';
import { Server, Socket } from 'socket.io';
+import { SecureContextOptions } from 'tls';
import { ServerUtils } from '../ServerUtils';
+import { serializedDoctype, serializedFieldsType } from '../fields/ObjectField';
import { logPort } from './ActionUtilities';
import { Client } from './Client';
import { DashStats } from './DashStats';
import { DocumentsCollection } from './IDatabase';
-import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from './Message';
-import { Search } from './Search';
+import { Diff, GestureContent, MessageStore } from './Message';
import { resolvedPorts, socketMap, timeMap, userOperations } from './SocketData';
-import { GoogleCredentialsLoader } from './apis/google/CredentialsLoader';
-import YoutubeApi from './apis/youtube/youtubeApiSample';
import { initializeGuest } from './authentication/DashUserModel';
import { Database } from './database';
export namespace WebSocket {
let CurUser: string | undefined;
- // eslint-disable-next-line import/no-mutable-exports
export let _socket: Socket;
- // eslint-disable-next-line import/no-mutable-exports
- export let _disconnect: Function;
+ export let _disconnect: () => void;
export const clients: { [key: string]: Client } = {};
function processGesturePoints(socket: Socket, content: GestureContent) {
socket.broadcast.emit('receiveGesturePoints', content);
}
- function processOverlayTrigger(socket: Socket, content: MobileInkOverlayContent) {
- socket.broadcast.emit('receiveOverlayTrigger', content);
- }
-
- function processUpdateOverlayPosition(socket: Socket, content: UpdateMobileInkOverlayPositionContent) {
- socket.broadcast.emit('receiveUpdateOverlayPosition', content);
- }
-
- function processMobileDocumentUpload(socket: Socket, content: MobileDocumentUploadContent) {
- socket.broadcast.emit('receiveMobileDocumentUpload', content);
- }
-
- function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) {
- const { ProjectCredentials } = GoogleCredentialsLoader;
- switch (query.type) {
- case YoutubeQueryTypes.Channels:
- YoutubeApi.authorizedGetChannel(ProjectCredentials);
- break;
- case YoutubeQueryTypes.SearchVideo:
- YoutubeApi.authorizedGetVideos(ProjectCredentials, query.userInput, callback);
- break;
- case YoutubeQueryTypes.VideoDetails:
- YoutubeApi.authorizedGetVideoDetails(ProjectCredentials, query.videoIds, callback);
- break;
- default:
- }
- }
-
export async function doDelete(onlyFields = true) {
const target: string[] = [];
onlyFields && target.push(DocumentsCollection);
await Database.Instance.dropSchema(...target);
- if (process.env.DISABLE_SEARCH !== 'true') {
- await Search.clear();
- }
initializeGuest();
}
@@ -82,137 +48,59 @@ export namespace WebSocket {
DashStats.logUserLogin(userEmail);
}
- function getField([id, callback]: [string, (result?: Transferable) => void]) {
- Database.Instance.getDocument(id, (result?: Transferable) => callback(result));
- }
-
- function getFields([ids, callback]: [string[], (result: Transferable[]) => void]) {
- Database.Instance.getDocuments(ids, callback);
- }
-
- function setField(socket: Socket, newValue: Transferable) {
- Database.Instance.update(newValue.id, newValue, () => socket.broadcast.emit(MessageStore.SetField.Message, newValue)); // broadcast set value to all other clients
- if (newValue.type === Types.Text) {
- // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
- Search.updateDocument({ id: newValue.id, data: { set: (newValue as any).data } });
- }
- }
-
- function GetRefFieldLocal([id, callback]: [string, (result?: Transferable) => void]) {
+ function GetRefFieldLocal(id: string, callback: (result?: serializedDoctype | undefined) => void) {
return Database.Instance.getDocument(id, callback);
}
- function GetRefField([id, callback]: [string, (result?: Transferable) => void]) {
+ function GetRefField([id, callback]: [string, (result?: serializedDoctype) => void]) {
process.stdout.write(`+`);
- GetRefFieldLocal([id, callback]);
+ GetRefFieldLocal(id, callback);
}
- function GetRefFields([ids, callback]: [string[], (result?: Transferable[]) => void]) {
+ function GetRefFields([ids, callback]: [string[], (result?: serializedDoctype[]) => void]) {
process.stdout.write(`${ids.length}…`);
Database.Instance.getDocuments(ids, callback);
}
- const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = {
- number: '_n',
- string: '_t',
- boolean: '_b',
- image: ['_t', 'url'],
- video: ['_t', 'url'],
- pdf: ['_t', 'url'],
- audio: ['_t', 'url'],
- web: ['_t', 'url'],
- map: ['_t', 'url'],
- script: ['_t', value => value.script.originalScript],
- RichTextField: ['_t', value => value.Text],
- date: ['_d', value => new Date(value.date).toISOString()],
- proxy: ['_i', 'fieldId'],
- list: [
- '_l',
- list => {
- const results: any[] = [];
- // eslint-disable-next-line no-use-before-define
- list.fields.forEach((value: any) => ToSearchTerm(value) && results.push(ToSearchTerm(value)!.value));
- return results.length ? results : null;
- },
- ],
- };
-
- function ToSearchTerm(valIn: any): { suffix: string; value: any } | undefined {
- let val = valIn;
- if (val === null || val === undefined) {
- return undefined;
- }
- const type = val.__type || typeof val;
-
- let suffix = suffixMap[type];
- if (!suffix) {
- return undefined;
- }
- if (Array.isArray(suffix)) {
- const accessor = suffix[1];
- if (typeof accessor === 'function') {
- val = accessor(val);
- } else {
- val = val[accessor];
- }
- [suffix] = suffix;
- }
- return { suffix, value: val };
- }
-
- function getSuffix(value: string | [string, any]): string {
- return typeof value === 'string' ? value : value[0];
- }
const pendingOps = new Map<string, { diff: Diff; socket: Socket }[]>();
- function dispatchNextOp(id: string) {
- const next = pendingOps.get(id)!.shift();
+ function dispatchNextOp(id: string): unknown {
+ const next = pendingOps.get(id)?.shift();
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const nextOp = (res: boolean) => dispatchNextOp(id);
if (next) {
const { diff, socket } = next;
- if (diff.diff.$addToSet) {
- // eslint-disable-next-line no-use-before-define
- return GetRefFieldLocal([diff.id, (result?: Transferable) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
+ // ideally, we'd call the Database update method for all actions, but for now we handle list insertion/removal on our own
+ switch (diff.diff.$addToSet ? 'add' : diff.diff.$remFromSet ? 'rem' : 'set') {
+ case 'add': return GetRefFieldLocal(id, (result) => addToListField(socket, diff, result, nextOp)); // prettier-ignore
+ case 'rem': return GetRefFieldLocal(id, (result) => remFromListField(socket, diff, result, nextOp)); // prettier-ignore
+ default: return Database.Instance.update(id, diff.diff,
+ () => nextOp(socket.broadcast.emit(MessageStore.UpdateField.Message, diff)),
+ false
+ ); // prettier-ignore
}
- if (diff.diff.$remFromSet) {
- // eslint-disable-next-line no-use-before-define
- return GetRefFieldLocal([diff.id, (result?: Transferable) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
- }
- // eslint-disable-next-line no-use-before-define
- return SetField(socket, diff);
}
- return !pendingOps.get(id)!.length && pendingOps.delete(id);
+ return !pendingOps.get(id)?.length && pendingOps.delete(id);
}
- function addToListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): void {
- const diff = diffIn;
- diff.diff.$set = diff.diff.$addToSet;
- delete diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
- const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
- const newListItems = diff.diff.$set[updatefield]?.fields;
- if (!newListItems) {
- console.log('Error: addToListField - no new list items');
- return;
- }
- const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((item: any) => item !== undefined) || [];
- diff.diff.$set[updatefield].fields = [...curList, ...newListItems]; // , ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
- const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
- delete diff.diff.length;
- Database.Instance.update(
- diff.id,
- diff.diff,
- () => {
- if (sendBack) {
- console.log('Warning: list modified during update. Composite list is being returned.');
- const { id } = socket;
- (socket as any).id = ''; // bcz: HACK. this prevents the update message from going back to the client that made the change.
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- (socket as any).id = id;
- } else {
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- }
- dispatchNextOp(diff.id);
- },
- false
- );
+ function addToListField(socket: Socket, diff: Diff, listDoc: serializedDoctype | undefined, cb: (res: boolean) => void): void {
+ const $addToSet = diff.diff.$addToSet as serializedFieldsType;
+ const updatefield = Array.from(Object.keys($addToSet ?? {}))[0];
+ const newListItems = $addToSet?.[updatefield]?.fields;
+
+ if (newListItems) {
+ const length = diff.diff.$addToSet?.length;
+ diff.diff.$set = $addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
+ delete diff.diff.$addToSet; // can't pass $set to Mongo, or it will do that insetead of $addToSet
+ const listItems = listDoc?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(item => item) ?? [];
+ diff.diff.$set[updatefield]!.fields = [...listItems, ...newListItems]; // , ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+
+ // if the client's list length is not the same as what we're writing to the server,
+ // then we need to send the server's version back to the client so that they are in synch.
+ // this could happen if another client made a change before the server receives the update from the first client
+ const target = length !== diff.diff.$set[updatefield].fields.length ? socket : socket.broadcast;
+ target === socket && console.log('Warning: SEND BACK: list modified during add update. Composite list is being returned.');
+ Database.Instance.update(diff.id, diff.diff, () => cb(target.emit(MessageStore.UpdateField.Message, diff)), false);
+ } else cb(false);
}
/**
@@ -227,7 +115,7 @@ export namespace WebSocket {
* the data
* @returns the closest index with the same value or -1 if the element was not found.
*/
- function findClosestIndex(list: any, indexesToDelete: number[], value: any, hintIndex: number) {
+ function findClosestIndex(list: { fieldId: string; __type: string }[], indexesToDelete: number[], value: { fieldId: string; __type: string }, hintIndex: number) {
let closestIndex = -1;
for (let i = 0; i < list.length; i++) {
if (list[i] === value && !indexesToDelete.includes(i)) {
@@ -251,140 +139,81 @@ export namespace WebSocket {
* items to delete)
* @param curListItems the server's current copy of the data
*/
- function remFromListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): void {
- const diff = diffIn;
- diff.diff.$set = diff.diff.$remFromSet;
- delete diff.diff.$remFromSet;
- const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
- const remListItems = diff.diff.$set[updatefield].fields;
- const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((f: any) => f !== null) || [];
- const { hint } = diff.diff.$set;
-
- if (hint) {
- // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements)
- const indexesToRemove: number[] = [];
- for (let i = 0; i < hint.deleteCount; i++) {
- if (curList.length > i + hint.start && _.isEqual(curList[i + hint.start], remListItems[i])) {
- indexesToRemove.push(i + hint.start);
- } else {
- const closestIndex = findClosestIndex(curList, indexesToRemove, remListItems[i], i + hint.start);
- if (closestIndex !== -1) {
- indexesToRemove.push(closestIndex);
+ function remFromListField(socket: Socket, diff: Diff, curListItems: serializedDoctype | undefined, cb: (res: boolean) => void): void {
+ const $remFromSet = diff.diff.$remFromSet as serializedFieldsType;
+ const updatefield = Array.from(Object.keys($remFromSet ?? {}))[0];
+ const remListItems = $remFromSet?.[updatefield]?.fields;
+
+ if (remListItems) {
+ const hint = diff.diff.$remFromSet?.hint;
+ const length = diff.diff.$remFromSet?.length;
+ diff.diff.$set = $remFromSet; // convert rem from set to a query of the current fields, and then a set of the old fields minus the removed ones
+ delete diff.diff.$remFromSet; // can't pass $set to Mongo, or it will do that insetead of $remFromSet
+ const curList = curListItems?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(f => f) ?? [];
+
+ if (hint) {
+ // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements)
+ const indexesToRemove: number[] = [];
+ for (let i = 0; i < hint.deleteCount; i++) {
+ if (curList.length > i + hint.start && _.isEqual(curList[i + hint.start], remListItems[i])) {
+ indexesToRemove.push(i + hint.start);
} else {
- console.log('Item to delete was not found - index = -1');
+ const closestIndex = findClosestIndex(curList, indexesToRemove, remListItems[i], i + hint.start);
+ if (closestIndex !== -1) {
+ indexesToRemove.push(closestIndex);
+ } else {
+ console.log('Item to delete was not found');
+ }
}
}
+ diff.diff.$set[updatefield]!.fields = curList.filter((curItem, index) => !indexesToRemove.includes(index));
+ } else {
+ // if we didn't get a hint, remove all matching items from the list
+ diff.diff.$set[updatefield]!.fields = curList?.filter(curItem => !remListItems.some(remItem => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem)));
}
- diff.diff.$set[updatefield].fields = curList?.filter((curItem: any, index: number) => !indexesToRemove.includes(index));
- } else {
- // go back to the original way to delete if we didn't receive
- // a hint from the client
- diff.diff.$set[updatefield].fields = curList?.filter(
- (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))
- );
- }
-
- // if the client and server have different versions of the data after
- // deletion, they will have different lengths and the server will
- // send its version of the data to the client
- const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
- delete diff.diff.length;
- Database.Instance.update(
- diff.id,
- diff.diff,
- () => {
- if (sendBack) {
- // the two copies are different, so the server sends its copy.
- console.log('SEND BACK');
- const { id } = socket;
- (socket as any).id = ''; // bcz: HACK. this prevents the update message from going back to the client that made the change.
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- (socket as any).id = id;
- } else {
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- }
- dispatchNextOp(diff.id);
- },
- false
- );
+ // if the client's list length is not the same as what we're writing to the server,
+ // then we need to send the server's version back to the client so that they are in synch.
+ // this could happen if another client made a change before the server receives the update from the first client
+ const target = length !== diff.diff.$set[updatefield].fields.length ? socket : socket.broadcast;
+ target === socket && console.log('Warning: SEND BACK: list modified during remove update. Composite list is being returned.');
+ Database.Instance.update(diff.id, diff.diff, () => cb(target.emit(MessageStore.UpdateField.Message, diff)), false);
+ } else cb(false);
}
function UpdateField(socket: Socket, diff: Diff) {
const curUser = socketMap.get(socket);
- if (!curUser) return false;
- const currentUsername = curUser.split(' ')[0];
- userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0);
+ if (curUser) {
+ const currentUsername = curUser.split(' ')[0];
+ userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0);
- if (CurUser !== socketMap.get(socket)) {
- CurUser = socketMap.get(socket);
- console.log('Switch User: ' + CurUser);
- }
- if (pendingOps.has(diff.id)) {
- pendingOps.get(diff.id)!.push({ diff, socket });
- return true;
- }
- pendingOps.set(diff.id, [{ diff, socket }]);
- if (diff.diff.$addToSet) {
- return GetRefFieldLocal([diff.id, (result?: Transferable) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
- }
- if (diff.diff.$remFromSet) {
- return GetRefFieldLocal([diff.id, (result?: Transferable) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
- }
- // eslint-disable-next-line no-use-before-define
- return SetField(socket, diff);
- }
- function SetField(socket: Socket, diff: Diff /* , curListItems?: Transferable */) {
- Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
- const docfield = diff.diff.$set || diff.diff.$unset;
- if (docfield) {
- const update: any = { id: diff.id };
- let dynfield = false;
- // eslint-disable-next-line no-restricted-syntax
- for (let key in docfield) {
- // eslint-disable-next-line no-continue
- if (!key.startsWith('fields.')) continue;
- dynfield = true;
- const val = docfield[key];
- key = key.substring(7);
- Object.values(suffixMap).forEach(suf => {
- update[key + getSuffix(suf)] = { set: null };
- });
- const term = ToSearchTerm(val);
- if (term !== undefined) {
- const { suffix, value } = term;
- update[key + suffix] = { set: value };
- if (key.endsWith('modificationDate')) {
- update['modificationDate' + suffix] = value;
- }
- }
+ if (CurUser !== socketMap.get(socket)) {
+ CurUser = socketMap.get(socket);
+ console.log('Switch User: ' + CurUser);
}
- if (dynfield) {
- Search.updateDocument(update);
+ if (pendingOps.has(diff.id)) {
+ pendingOps.get(diff.id)!.push({ diff, socket });
+ return true;
}
+ pendingOps.set(diff.id, [{ diff, socket }]);
+ return dispatchNextOp(diff.id);
}
- dispatchNextOp(diff.id);
+ return false;
}
function DeleteField(socket: Socket, id: string) {
- Database.Instance.delete({ _id: id }).then(() => {
- socket.broadcast.emit(MessageStore.DeleteField.Message, id);
- });
-
- Search.deleteDocuments([id]);
+ Database.Instance.delete({ _id: id }).then(() => socket.broadcast.emit(MessageStore.DeleteField.Message, id));
}
function DeleteFields(socket: Socket, ids: string[]) {
- Database.Instance.delete({ _id: { $in: ids } }).then(() => {
- socket.broadcast.emit(MessageStore.DeleteFields.Message, ids);
- });
- Search.deleteDocuments(ids);
+ Database.Instance.delete({ _id: { $in: ids } }).then(() => socket.broadcast.emit(MessageStore.DeleteFields.Message, ids));
}
- function CreateField(newValue: any) {
+ function CreateDocField(newValue: serializedDoctype) {
Database.Instance.insert(newValue);
}
- export async function initialize(isRelease: boolean, credentials: any) {
+
+ export async function initialize(isRelease: boolean, credentials: SecureContextOptions) {
let io: Server;
if (isRelease) {
const { socketPort } = process.env;
@@ -417,21 +246,19 @@ export namespace WebSocket {
socket.in(room).emit('message', message);
});
- socket.on('ipaddr', () => {
+ socket.on('ipaddr', () =>
networkInterfaces().keys?.forEach(dev => {
if (dev.family === 'IPv4' && dev.address !== '127.0.0.1') {
socket.emit('ipaddr', dev.address);
}
- });
- });
+ })
+ );
- socket.on('bye', () => {
- console.log('received bye');
- });
+ socket.on('bye', () => console.log('received bye'));
socket.on('disconnect', () => {
const currentUser = socketMap.get(socket);
- if (!(currentUser === undefined)) {
+ if (currentUser !== undefined) {
const currentUsername = currentUser.split(' ')[0];
DashStats.logUserLogout(currentUsername);
delete timeMap[currentUsername];
@@ -441,22 +268,15 @@ export namespace WebSocket {
ServerUtils.Emit(socket, MessageStore.Foo, 'handshooken');
ServerUtils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid));
- ServerUtils.AddServerHandler(socket, MessageStore.SetField, args => setField(socket, args));
- ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
- ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
if (isRelease) {
ServerUtils.AddServerHandler(socket, MessageStore.DeleteAll, () => doDelete(false));
}
- ServerUtils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
- ServerUtils.AddServerHandlerCallback(socket, MessageStore.YoutubeApiQuery, HandleYoutubeQuery);
+ ServerUtils.AddServerHandler(socket, MessageStore.CreateDocField, CreateDocField);
ServerUtils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff));
ServerUtils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id));
ServerUtils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids));
ServerUtils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.MobileInkOverlayTrigger, content => processOverlayTrigger(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.UpdateMobileInkOverlayPosition, content => processUpdateOverlayPosition(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.MobileDocumentUpload, content => processMobileDocumentUpload(socket, content));
ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);