|
@@ -8,7 +8,8 @@ import { ConnectionState, Transport, TransportEvent, TransportMessage } from '..
|
|
|
import { EventMessage, FisMessage } from '../interface/transport.interface';
|
|
|
import { WrappedMessage } from './message.ordering';
|
|
|
import axios, { AxiosError, AxiosResponse } from 'axios';
|
|
|
-import { error } from 'console';
|
|
|
+import ConsoleLogger from './log.utils';
|
|
|
+const console: ConsoleLogger = new ConsoleLogger(`HttpUtils`, ['transport'])
|
|
|
|
|
|
export function startHttpServer(port: number): Observable<ConnectedHttpClient> {
|
|
|
return new Observable((observer: Observer<ConnectedHttpClient>) => {
|
|
@@ -18,7 +19,7 @@ export function startHttpServer(port: number): Observable<ConnectedHttpClient> {
|
|
|
app.use(express.json());
|
|
|
|
|
|
app.listen(port, () => {
|
|
|
- console.log(`Server running at http://localhost:${port}`);
|
|
|
+ console.log({ message: `Server running at http://localhost:${port}` });
|
|
|
});
|
|
|
|
|
|
observer.next({
|
|
@@ -36,34 +37,32 @@ export async function initiateClientToServer(url: string, event: Subject<Transpo
|
|
|
// logic here for using browser fetch
|
|
|
} else { // axios methods
|
|
|
if (receiverProfileInfo) {
|
|
|
- console.log(`Is Old profile, reconnecting with server`)
|
|
|
+ console.log({ message: `Is Old profile, reconnecting with server` })
|
|
|
checkOwnClientInfo(receiverProfileInfo.id).then((profile: ConnectedHttpServer) => {
|
|
|
receiverProfileInfo!.id = profile.id
|
|
|
- console.log(`jsonfile.`, profile)
|
|
|
- postAxiosRequest(url + '/profile', { name: 'Old Client', data: profile }).then((profileInfo: { name: string, message: { id: string } }) => {
|
|
|
- writeFile(profileInfo.message).then((data: any) => {
|
|
|
- console.log(`Assigned new client Id: ${(data.message as ConnectedHttpServer).id}`)
|
|
|
- receiverProfileInfo = data.message as ConnectedHttpServer
|
|
|
- writeFile(data.message).then(() => {
|
|
|
- event.next({
|
|
|
- id: uuidv4(),
|
|
|
- event: 'Server Connected',
|
|
|
- data: {
|
|
|
- clientId: (data.message as ConnectedHttpServer).id,
|
|
|
- message: `Existing Http Channel ${(data.message as ConnectedHttpServer).id} re-established.`
|
|
|
- } as EventMessage
|
|
|
- })
|
|
|
- })
|
|
|
- // Update Http instance record
|
|
|
- let clientObj: ConnectedHttpServer | undefined = connectedHttpServers.find(obj => obj.id === (data.message as ConnectedHttpServer).id)
|
|
|
- if (clientObj) {
|
|
|
- receiverProfileInfo.connectionState = clientObj.connectionState
|
|
|
- clientObj.connectionState.next('ONLINE')
|
|
|
- resolve(clientObj)
|
|
|
- }
|
|
|
+ // console.log({ message: 'jsonfile', details: profile })
|
|
|
+ postAxiosRequest(url + '/profile', { name: 'Old Client', message: profile }).then((profileInfo: { name: string, message: { id: string } }) => {
|
|
|
+ console.log({ message: `Acknowledged as previous client. Id: ${profileInfo.message.id}` })
|
|
|
+ event.next({
|
|
|
+ id: uuidv4(),
|
|
|
+ event: 'Server Connected',
|
|
|
+ data: {
|
|
|
+ clientId: profileInfo.message.id,
|
|
|
+ message: `Existing Http Channel ${profileInfo.message.id} re-established.`
|
|
|
+ } as EventMessage
|
|
|
})
|
|
|
+ // Update Http instance record
|
|
|
+ let clientObj: ConnectedHttpServer | undefined = connectedHttpServers.find(obj => obj.id === profileInfo.message.id)
|
|
|
+ console.log({ message: 'ClientObj', details: clientObj })
|
|
|
+ console.log({ message: 'ReceiverProfile', details: receiverProfileInfo })
|
|
|
+ if (clientObj) {
|
|
|
+ clientObj.connectionState.next('ONLINE')
|
|
|
+ console.log({ message: receiverProfileInfo.connectionState.getValue() })
|
|
|
+ resolve(clientObj)
|
|
|
+ }
|
|
|
+
|
|
|
}).catch((error: AxiosError) => {
|
|
|
- reject(error)
|
|
|
+ reject({ error: error, objRef: receiverProfileInfo })
|
|
|
})
|
|
|
}).catch((error) => {
|
|
|
console.error(error)
|
|
@@ -72,9 +71,9 @@ export async function initiateClientToServer(url: string, event: Subject<Transpo
|
|
|
resolve(receiverProfileInfo)
|
|
|
})
|
|
|
}).catch((error) => {
|
|
|
- reject(error)
|
|
|
+ reject({ error: error, objRef: receiverProfileInfo })
|
|
|
})
|
|
|
- reject(error)
|
|
|
+ reject({ error: error, objRef: receiverProfileInfo })
|
|
|
})
|
|
|
} else {
|
|
|
postAxiosRequest(url + '/profile', { name: 'New Client', data: null }).then((profileInfo: { name: string, message: any }) => {
|
|
@@ -82,16 +81,16 @@ export async function initiateClientToServer(url: string, event: Subject<Transpo
|
|
|
resolve(receiverProfileInfo)
|
|
|
})
|
|
|
}).catch((error) => {
|
|
|
- reject(error)
|
|
|
+ reject({ error: error, objRef: receiverProfileInfo })
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+// For client usage
|
|
|
export function handleClientHttpConnection(url: string, server: ConnectedHttpServer): Observable<TransportEvent> {
|
|
|
- return new Observable((observer: Observer<TransportEvent>) => {
|
|
|
+ return new Observable((eventNotification: Observer<TransportEvent>) => {
|
|
|
server.connectionState.next('ONLINE');
|
|
|
let active: boolean = true; // Flag to control polling lifecycle
|
|
|
|
|
@@ -99,13 +98,15 @@ export function handleClientHttpConnection(url: string, server: ConnectedHttpSer
|
|
|
while (active) {
|
|
|
try {
|
|
|
// Axios request with timeout
|
|
|
+ // const response = await axios.get(`${url}/poll`); // removing the timeout temporarily.
|
|
|
const response = await axios.get(`${url}/poll`, {
|
|
|
- timeout: 10000, // 10s timeout
|
|
|
+ timeout: 3000, // 10s timeout this one will trigger error. That's why it keeps on throwing error
|
|
|
});
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
- const data = response.data as WrappedMessage;
|
|
|
- observer.next({
|
|
|
+ const data = response.data;
|
|
|
+ console.log({ message: 'Long Poll Response', details: data })
|
|
|
+ eventNotification.next({
|
|
|
id: uuidv4(),
|
|
|
event: 'New Message',
|
|
|
data: {
|
|
@@ -113,17 +114,19 @@ export function handleClientHttpConnection(url: string, server: ConnectedHttpSer
|
|
|
dateCreated: new Date(),
|
|
|
transport: Transport.Http,
|
|
|
target: server.id,
|
|
|
- payload: data,
|
|
|
+ payload: data.message,
|
|
|
} as TransportMessage,
|
|
|
});
|
|
|
} else if (response.status === 204) {
|
|
|
- console.log('No new messages from the server.');
|
|
|
+ console.log({ message: 'No new messages from the server.' });
|
|
|
} else {
|
|
|
- handleServerConnectionError(active, observer, server)
|
|
|
+ console.error({ message: `Unexpected response status: ${response.status}` })
|
|
|
+ handleServerConnectionError(active, eventNotification, server)
|
|
|
throw new Error(`Unexpected response status: ${response.status}`);
|
|
|
}
|
|
|
} catch (error: unknown) {
|
|
|
- handleServerConnectionError(active, observer, server)
|
|
|
+ console.error({ message: `Unknown Error.`, details: error }) // culprit is here
|
|
|
+ handleServerConnectionError(active, eventNotification, server)
|
|
|
// Error handling with server disconnect notification
|
|
|
let errorMessage: string;
|
|
|
|
|
@@ -141,7 +144,7 @@ export function handleClientHttpConnection(url: string, server: ConnectedHttpSer
|
|
|
errorMessage = 'An unknown error occurred during polling.';
|
|
|
}
|
|
|
|
|
|
- console.error(`Polling error: ${errorMessage}`);
|
|
|
+ console.error({ message: `Polling error: ${errorMessage}` });
|
|
|
// observer.error(new Error(errorMessage)); // Notify subscribers of the error
|
|
|
break; // Stop polling on error
|
|
|
}
|
|
@@ -150,18 +153,17 @@ export function handleClientHttpConnection(url: string, server: ConnectedHttpSer
|
|
|
|
|
|
longPoll();
|
|
|
|
|
|
-
|
|
|
-
|
|
|
// Cleanup logic for unsubscribing
|
|
|
return () => {
|
|
|
- console.log('Unsubscribed from the long-polling channel.');
|
|
|
- observer.complete(); // Notify completion
|
|
|
+ console.log({ message: 'Unsubscribed from the long-polling channel.' });
|
|
|
+ eventNotification.complete(); // Notify completion
|
|
|
};
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function handleServerConnectionError(active: boolean, observer: Observer<TransportEvent>, server: ConnectedHttpServer): void {
|
|
|
- console.log('Server lost connection');
|
|
|
+ server.connectionState.next('OFFLINE');
|
|
|
+ console.log({ message: 'Server lost connection' });
|
|
|
active = false; // Stop polling
|
|
|
observer.next({
|
|
|
id: uuidv4(),
|
|
@@ -171,18 +173,16 @@ function handleServerConnectionError(active: boolean, observer: Observer<Transpo
|
|
|
message: '',
|
|
|
payload: {
|
|
|
time: new Date(),
|
|
|
+ objRef: server
|
|
|
},
|
|
|
} as EventMessage,
|
|
|
});
|
|
|
- server.connectionState.next('OFFLINE');
|
|
|
+ observer.complete()
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
async function updateProfileAndPublishEvent(receiverProfileInfo: ConnectedHttpServer | undefined, profile: { name: string, message: any }, event: Subject<TransportEvent>, connectedHttpServers: ConnectedHttpServer[]): Promise<ConnectedHttpServer> {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
- console.log(`Assigned client Name: ${(profile.message as ConnectedHttpServer).id}`)
|
|
|
+ console.log({ message: `Assigned client Name: ${(profile.message as ConnectedHttpServer).id}` })
|
|
|
receiverProfileInfo = profile.message as ConnectedHttpServer
|
|
|
writeFile(profile.message).then(() => {
|
|
|
event.next({
|
|
@@ -193,6 +193,15 @@ async function updateProfileAndPublishEvent(receiverProfileInfo: ConnectedHttpSe
|
|
|
message: `New Http Channel ${(profile.message as ConnectedHttpServer).id} established.`
|
|
|
} as EventMessage
|
|
|
})
|
|
|
+ // broadcast event to allow retransmission to relase buffered messages
|
|
|
+ event.next({
|
|
|
+ id: uuidv4(),
|
|
|
+ event: `Server Connected`,
|
|
|
+ data: {
|
|
|
+ clientId: (profile.message as ConnectedHttpServer).id,
|
|
|
+ message: `Server ${(profile.message as ConnectedHttpServer).id} connected and ready to go.`
|
|
|
+ } as EventMessage
|
|
|
+ })
|
|
|
}).catch((error) => {
|
|
|
reject(error)
|
|
|
})
|
|
@@ -212,13 +221,13 @@ async function postAxiosRequest(url: string, data: any): Promise<any> {
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
try {
|
|
|
const response: AxiosResponse<any> = await axios.post(url, data);
|
|
|
- console.log('Response:', response.data);
|
|
|
+ console.log({ message: 'Response', details: response.data });
|
|
|
resolve(response.data)
|
|
|
} catch (error) {
|
|
|
if (axios.isAxiosError(error)) {
|
|
|
- console.error('Axios Error:', error.code);
|
|
|
+ console.error({ message: 'Axios Error:', details: error.code });
|
|
|
} else {
|
|
|
- console.error('Unexpected Error:', error);
|
|
|
+ console.error({ message: 'Unexpected Error:', details: error });
|
|
|
}
|
|
|
reject(error)
|
|
|
}
|
|
@@ -263,7 +272,8 @@ function handleProfile(app: Express, data: { name: `Old Client` | `New Client`,
|
|
|
connectedClientHttp.push(clientInstance)
|
|
|
addClientToDB(clientInstance)
|
|
|
startListeningAndStreaming(app, clientInstance, event)
|
|
|
- } else {
|
|
|
+ } else if (data.name == 'Old Client') {
|
|
|
+ console.log({ message: `Is old client`, details: data })
|
|
|
// update first
|
|
|
let clientInstance: ConnectedHttpClient | undefined
|
|
|
if (connectedClientHttp.length > 0) {
|
|
@@ -278,7 +288,7 @@ function handleProfile(app: Express, data: { name: `Old Client` | `New Client`,
|
|
|
}
|
|
|
function handleFoundClient(clientInstance: ConnectedHttpClient | undefined): void {
|
|
|
if (clientInstance) {
|
|
|
- console.log(`Http Client ${clientInstance.id} Found`)
|
|
|
+ console.log({ message: `Http Client ${clientInstance.id} Found` })
|
|
|
res.json({ name: 'Adjusted Profile', message: { id: clientInstance.id } })
|
|
|
// replace socket instance since the previous has been terminated
|
|
|
clientInstance.instance = app
|
|
@@ -287,7 +297,7 @@ function handleProfile(app: Express, data: { name: `Old Client` | `New Client`,
|
|
|
clientInstance.connectionState = new BehaviorSubject<ConnectionState>(`OFFLINE`)
|
|
|
}
|
|
|
// need to start listening again, because it's assigned a different socket instance this time round
|
|
|
- startListeningAndStreaming(app, clientInstance, event)
|
|
|
+ startListeningAndStreaming(app, clientInstance, event, true)
|
|
|
event.next({
|
|
|
id: uuidv4(),
|
|
|
event: 'Client Connected',
|
|
@@ -299,7 +309,7 @@ function handleProfile(app: Express, data: { name: `Old Client` | `New Client`,
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
- console.log(`Profile Not Found`)
|
|
|
+ console.log({ message: `Profile Not Found` })
|
|
|
res.json({ name: 'Error', message: 'Receiver Profile Not found' })
|
|
|
}
|
|
|
}
|
|
@@ -313,7 +323,7 @@ export async function checkIfClientExists(id: string, filePath: string = 'client
|
|
|
try {
|
|
|
// Check if the file exists
|
|
|
if (!fs.existsSync(filePath)) {
|
|
|
- console.log("File does not exist.");
|
|
|
+ console.log({ message: "File does not exist." });
|
|
|
reject('File does not exist');
|
|
|
}
|
|
|
|
|
@@ -325,14 +335,14 @@ export async function checkIfClientExists(id: string, filePath: string = 'client
|
|
|
let obj = data.find(entry => entry.id === id);
|
|
|
|
|
|
if (obj) {
|
|
|
- console.log(`Client with ID ${id} exists.`);
|
|
|
+ console.log({ message: `Client with ID ${id} exists.` });
|
|
|
} else {
|
|
|
- console.log(`Client with ID ${id} does not exist.`);
|
|
|
+ console.log({ message: `Client with ID ${id} does not exist.` });
|
|
|
}
|
|
|
|
|
|
resolve(obj);
|
|
|
} catch (error) {
|
|
|
- console.error('Error reading the file:', error);
|
|
|
+ console.error({ message: 'Error reading the file:', details: error });
|
|
|
reject(`Error reading the file`)
|
|
|
}
|
|
|
})
|
|
@@ -359,14 +369,14 @@ export function addClientToDB(entry: ConnectedHttpClient, filePath: string = 'cl
|
|
|
|
|
|
// Write the updated array back to the file
|
|
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
|
- console.log(`Entry added successfully.`);
|
|
|
+ console.log({ message: `Entry added successfully.` });
|
|
|
} catch (error) {
|
|
|
- console.error('Error writing to file:', error);
|
|
|
+ console.error({ message: 'Error writing to file:', details: error });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// this is for server usage only
|
|
|
-export function startListeningAndStreaming(app: Express, client: ConnectedHttpClient, eventListener: Observer<TransportEvent>): void {
|
|
|
+export function startListeningAndStreaming(app: Express, client: ConnectedHttpClient, eventListener: Observer<TransportEvent>, oldClient?: boolean): void {
|
|
|
/* Generally, we don't need this unless in the case of being the receiver */
|
|
|
app.post('/message', (req, res) => {
|
|
|
eventListener.next({
|
|
@@ -380,13 +390,24 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
payload: req.body
|
|
|
} as TransportMessage
|
|
|
})
|
|
|
- res.json(`Received ${(req.body as FisMessage)?.header?.messageID ?? `Undefined`}`)
|
|
|
+ res.json(`Received ${((req.body as WrappedMessage)?.payload as FisMessage)?.header?.messageID ?? `Undefined`}`)
|
|
|
})
|
|
|
|
|
|
app.get('/poll', (req, res) => {
|
|
|
- console.log('Client connected for long polling.');
|
|
|
+ console.log({ message: 'Client connected for long polling.' });
|
|
|
client.connectionState.next('ONLINE');
|
|
|
|
|
|
+ // notify it's associated retransmission to start releaseing buffer
|
|
|
+ eventListener.next({
|
|
|
+ id: uuidv4(),
|
|
|
+ event: oldClient ? 'Client Re-connected' : `Client Connected`,
|
|
|
+ data: {
|
|
|
+ clientId: client.id,
|
|
|
+ message: `Socket Client ${oldClient ? `Re-Connected` : `Connected`}. Adapter ID assigned: ${client.id}`,
|
|
|
+ payload: client
|
|
|
+ } as EventMessage
|
|
|
+ })
|
|
|
+
|
|
|
// Flag to track if the response has been sent
|
|
|
let responseSent = false;
|
|
|
|
|
@@ -394,7 +415,7 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
const subscription = client.responseStream.asObservable().subscribe({
|
|
|
next: (message: WrappedMessage) => {
|
|
|
if (!responseSent) {
|
|
|
- console.log(`Sending data to client: ${JSON.stringify(message)}`);
|
|
|
+ console.log({ message: `Sending data to client: ${JSON.stringify(message)}` });
|
|
|
res.json({ message }); // Send the data to the client
|
|
|
responseSent = true; // Mark response as sent
|
|
|
subscription.unsubscribe(); // Unsubscribe to close this request
|
|
@@ -402,7 +423,7 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
},
|
|
|
error: (err) => {
|
|
|
if (!responseSent) {
|
|
|
- console.error('Error in data stream:', err);
|
|
|
+ console.error({ message: 'Error in data stream:', details: err });
|
|
|
res.status(500).send('Internal Server Error');
|
|
|
responseSent = true; // Mark response as sent
|
|
|
}
|
|
@@ -410,7 +431,7 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
},
|
|
|
complete: () => {
|
|
|
if (!responseSent) {
|
|
|
- console.log('Data stream completed.');
|
|
|
+ console.log({ message: 'Data stream completed.' });
|
|
|
res.status(204).send(); // No Content
|
|
|
responseSent = true; // Mark response as sent
|
|
|
}
|
|
@@ -421,7 +442,7 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
// Timeout if no data is emitted within a specified duration
|
|
|
const timeout = setTimeout(() => {
|
|
|
if (!responseSent) {
|
|
|
- console.log('No data emitted. Sending timeout response.');
|
|
|
+ console.log({ message: 'No data emitted. Sending timeout response.' });
|
|
|
res.status(204).send(); // No Content
|
|
|
responseSent = true; // Mark response as sent
|
|
|
subscription.unsubscribe(); // Ensure cleanup
|
|
@@ -431,7 +452,7 @@ export function startListeningAndStreaming(app: Express, client: ConnectedHttpCl
|
|
|
// Handle client disconnection
|
|
|
res.on('close', () => {
|
|
|
if (!responseSent) {
|
|
|
- console.error(`Http Client ${client.id} disconnected`);
|
|
|
+ console.error({ message: `Http Client ${client.id} disconnected` });
|
|
|
eventListener.next({
|
|
|
id: uuidv4(),
|
|
|
event: 'Client Disconnected',
|
|
@@ -487,10 +508,10 @@ export async function writeFile(data: { id: string }): Promise<boolean> {
|
|
|
// Write JSON data to a file
|
|
|
fs.writeFile(`${data.id}.json`, JSON.stringify(data, null, 2), (err) => {
|
|
|
if (err) {
|
|
|
- console.error('Error writing file', err);
|
|
|
+ console.error({ message: 'Error writing file', details: err });
|
|
|
reject(false)
|
|
|
} else {
|
|
|
- console.log('File has been written');
|
|
|
+ console.log({ message: 'File has been written' });
|
|
|
resolve(true)
|
|
|
}
|
|
|
});
|