Add socket reconnect logic
This commit is contained in:
		
							parent
							
								
									d79fe6982f
								
							
						
					
					
						commit
						d280a91115
					
				| @ -34,11 +34,6 @@ | |||||||
|                                     Databases |                                     Databases | ||||||
|                                 </router-link> |                                 </router-link> | ||||||
|                             </li> |                             </li> | ||||||
|                             <li> |  | ||||||
|                                 <router-link :to="{ name: 'server-network' }"> |  | ||||||
|                                     Networking |  | ||||||
|                                 </router-link> |  | ||||||
|                             </li> |  | ||||||
|                         </ul> |                         </ul> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
| @ -49,7 +44,7 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="fixed pin-r pin-b m-6 max-w-sm" v-show="connectionError"> |         <div class="fixed pin-r pin-b m-6 max-w-sm" v-show="connectionError"> | ||||||
|             <div class="alert error"> |             <div class="alert error"> | ||||||
|                 There was an error while attempting to connect to the Daemon websocket. Error reported was: "{{connectionError.message}}" |                 There was an error while attempting to connect to the Daemon websocket. Error: {{connectionError}} | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -24,6 +24,21 @@ export default class SocketioConnector { | |||||||
|      */ |      */ | ||||||
|     store: Store<any> | undefined; |     store: Store<any> | undefined; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Tracks a reconnect attempt for the websocket. Will gradually back off on attempts | ||||||
|  |      * after a certain period of time has elapsed. | ||||||
|  |      */ | ||||||
|  |     private reconnectTimeout: any; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Tracks the number of reconnect attempts which is used to determine the backoff | ||||||
|  |      * throttle for connections. | ||||||
|  |      */ | ||||||
|  |     private reconnectAttempts: number = 0; | ||||||
|  | 
 | ||||||
|  |     private socketProtocol?: string; | ||||||
|  |     private socketUrl?: string; | ||||||
|  | 
 | ||||||
|     constructor(store: Store<any> | undefined) { |     constructor(store: Store<any> | undefined) { | ||||||
|         this.socket = null; |         this.socket = null; | ||||||
|         this.store = store; |         this.store = store; | ||||||
| @ -32,15 +47,23 @@ export default class SocketioConnector { | |||||||
|     /** |     /** | ||||||
|      * Initialize a new Socket connection. |      * Initialize a new Socket connection. | ||||||
|      */ |      */ | ||||||
|     connect(url: string, protocols?: string | string[]): void { |     public connect(url: string, protocol?: string): void { | ||||||
|         this.socket = new WebSocket(url, protocols); |         this.socketUrl = url; | ||||||
|         this.registerEventListeners(); |         this.socketProtocol = protocol; | ||||||
|  | 
 | ||||||
|  |         this.connectToSocket() | ||||||
|  |             .then(socket => { | ||||||
|  |                 this.socket = socket; | ||||||
|  |                 this.emitAndPassToStore(SOCKET_CONNECT); | ||||||
|  |                 this.registerEventListeners(); | ||||||
|  |             }) | ||||||
|  |             .catch(() => this.reconnectToSocket()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Return the socket instance we are working with. |      * Return the socket instance we are working with. | ||||||
|      */ |      */ | ||||||
|     instance(): WebSocket | null { |     public instance(): WebSocket | null { | ||||||
|         return this.socket; |         return this.socket; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -48,7 +71,7 @@ export default class SocketioConnector { | |||||||
|      * Sends an event along to the websocket. If there is no active connection, a void |      * Sends an event along to the websocket. If there is no active connection, a void | ||||||
|      * result is returned. |      * result is returned. | ||||||
|      */ |      */ | ||||||
|     emit(event: string, payload?: string | Array<string>): void | false { |     public emit(event: string, payload?: string | Array<string>): void | false { | ||||||
|         if (!this.socket) { |         if (!this.socket) { | ||||||
|             return false |             return false | ||||||
|         } |         } | ||||||
| @ -62,23 +85,23 @@ export default class SocketioConnector { | |||||||
|      * Register the event listeners for this socket including user-defined ones in the store as |      * Register the event listeners for this socket including user-defined ones in the store as | ||||||
|      * well as global system events from Socekt.io. |      * well as global system events from Socekt.io. | ||||||
|      */ |      */ | ||||||
|     registerEventListeners() { |     protected registerEventListeners() { | ||||||
|         if (!this.socket) { |         if (!this.socket) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.socket.onopen = () => this.emitAndPassToStore(SOCKET_CONNECT); |         this.socket.onclose = () => { | ||||||
|         this.socket.onclose = () => this.emitAndPassToStore(SOCKET_DISCONNECT); |             this.reconnectToSocket(); | ||||||
|  |             this.emitAndPassToStore(SOCKET_DISCONNECT); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         this.socket.onerror = () => { |         this.socket.onerror = () => { | ||||||
|             // @todo reconnect?
 |             if (this.socket && this.socket.readyState !== WebSocket.OPEN) { | ||||||
|             if (this.socket && this.socket.readyState !== 1) { |  | ||||||
|                 this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']); |                 this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         this.socket.onmessage = (wse): void => { |         this.socket.onmessage = (wse): void => { | ||||||
|             console.log('Socket message:', wse.data); |  | ||||||
| 
 |  | ||||||
|             try { |             try { | ||||||
|                 let {event, args}: WingsWebsocketResponse = JSON.parse(wse.data); |                 let {event, args}: WingsWebsocketResponse = JSON.parse(wse.data); | ||||||
| 
 | 
 | ||||||
| @ -91,10 +114,83 @@ export default class SocketioConnector { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Performs an actual socket connection, wrapped as a Promise for an easier interface. | ||||||
|  |      */ | ||||||
|  |     protected connectToSocket(): Promise<WebSocket> { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             let hasReturned = false; | ||||||
|  |             const socket = new WebSocket(this.socketUrl!, this.socketProtocol); | ||||||
|  | 
 | ||||||
|  |             socket.onopen = () => { | ||||||
|  |                 if (hasReturned) { | ||||||
|  |                     socket && socket.close(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 hasReturned = true; | ||||||
|  |                 this.resetConnectionAttempts(); | ||||||
|  |                 resolve(socket); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             const rejectFunc = () => { | ||||||
|  |                 if (!hasReturned) { | ||||||
|  |                     hasReturned = true; | ||||||
|  |                     this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']); | ||||||
|  |                     reject(); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             socket.onerror = rejectFunc; | ||||||
|  |             socket.onclose = rejectFunc; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Attempts to reconnect to the socket instance if it becomes disconnected. | ||||||
|  |      */ | ||||||
|  |     private reconnectToSocket() { | ||||||
|  |         const { socket } = this; | ||||||
|  |         if (!socket) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Clear the existing timeout if one exists for some reason.
 | ||||||
|  |         this.reconnectTimeout && clearTimeout(this.reconnectTimeout); | ||||||
|  | 
 | ||||||
|  |         this.reconnectTimeout = setTimeout(() => { | ||||||
|  |             console.warn(`Attempting to reconnect to websocket [${this.reconnectAttempts}]...`); | ||||||
|  | 
 | ||||||
|  |             this.reconnectAttempts++; | ||||||
|  |             this.connect(this.socketUrl!, this.socketProtocol); | ||||||
|  |         }, this.getIntervalTimeout()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private resetConnectionAttempts() { | ||||||
|  |         this.reconnectTimeout && clearTimeout(this.reconnectTimeout); | ||||||
|  |         this.reconnectAttempts = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Determine the amount of time we should wait before attempting to reconnect to the socket. | ||||||
|  |      */ | ||||||
|  |     private getIntervalTimeout(): number { | ||||||
|  |         if (this.reconnectAttempts < 10) { | ||||||
|  |             return 50; | ||||||
|  |         } else if (this.reconnectAttempts < 25) { | ||||||
|  |             return 500; | ||||||
|  |         } else if (this.reconnectAttempts < 50) { | ||||||
|  |             return 1000; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return 2500; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Emits the event over the event emitter and also passes it along to the vuex store. |      * Emits the event over the event emitter and also passes it along to the vuex store. | ||||||
|      */ |      */ | ||||||
|     emitAndPassToStore(event: string, payload?: Array<string>) { |     private emitAndPassToStore(event: string, payload?: Array<string>) { | ||||||
|         payload ? SocketEmitter.emit(event, ...payload) : SocketEmitter.emit(event); |         payload ? SocketEmitter.emit(event, ...payload) : SocketEmitter.emit(event); | ||||||
|         this.passToStore(event, payload); |         this.passToStore(event, payload); | ||||||
|     } |     } | ||||||
| @ -102,7 +198,7 @@ export default class SocketioConnector { | |||||||
|     /** |     /** | ||||||
|      * Pass event calls off to the Vuex store if there is a corresponding function. |      * Pass event calls off to the Vuex store if there is a corresponding function. | ||||||
|      */ |      */ | ||||||
|     passToStore(event: string, payload?: Array<string>) { |     private passToStore(event: string, payload?: Array<string>) { | ||||||
|         if (!this.store) { |         if (!this.store) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -126,7 +222,7 @@ export default class SocketioConnector { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     unwrap(args: Array<string>) { |     private unwrap(args: Array<string>) { | ||||||
|         return (args && args.length <= 1) ? args[0] : args; |         return (args && args.length <= 1) ? args[0] : args; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dane Everitt
						Dane Everitt