github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/kat/src/clients/app2app.ts (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import io from 'socket.io-client';
    16  import { config } from '../lib/config';
    17  import * as utils from '../lib/utils';
    18  import { AssetTradeMessage, IApp2AppMessage, IApp2AppMessageListener } from '../lib/interfaces';
    19  
    20  const log = utils.getLogger('clients/app2app.ts');
    21  
    22  let socket: SocketIOClient.Socket
    23  let listeners: IApp2AppMessageListener[] = [];
    24  
    25  export const init = async () => {
    26    establishSocketIOConnection();
    27  };
    28  
    29  function subscribeWithRetry() {
    30    log.trace(`App2App subscription: ${config.app2app.destinations.kat}`)
    31    socket.emit('subscribe', [config.app2app.destinations.kat], (err: any, data: any) => {
    32      if (err) {
    33        log.error(`App2App subscription failure (retrying): ${err}`);
    34        setTimeout(subscribeWithRetry, utils.constants.SUBSCRIBE_RETRY_INTERVAL);
    35        return;
    36      }
    37      log.trace(`App2App subscription succeeded: ${JSON.stringify(data)}`);
    38    });
    39  }
    40  
    41  const establishSocketIOConnection = () => {
    42    let error = false;
    43    const { APP2APP_BATCH_SIZE, APP2APP_BATCH_TIMEOUT, APP2APP_READ_AHEAD } = utils.constants;
    44    socket = io.connect(`${config.app2app.socketIOEndpoint}?auto_commit=false&read_ahead=${APP2APP_READ_AHEAD}&batch_size=${APP2APP_BATCH_SIZE}&batch_timeout=${APP2APP_BATCH_TIMEOUT}`, {
    45      transportOptions: {
    46        polling: {
    47          extraHeaders: {
    48            Authorization: 'Basic ' + Buffer.from(`${config.appCredentials.user}` +
    49              `:${config.appCredentials.password}`).toString('base64')
    50          }
    51        }
    52      }
    53    }).on('connect', () => {
    54      if (error) {
    55        error = false;
    56        log.info('App2App messaging Socket IO connection restored');
    57      }
    58      subscribeWithRetry();
    59    }).on('connect_error', (err: Error) => {
    60      error = true;
    61      log.error(`App2App messaging Socket IO connection error. ${err.toString()}`);
    62    }).on('error', (err: Error) => {
    63      error = true;
    64      log.error(`App2app messaging Socket IO error. ${err.toString()}`);
    65    }).on('exception', (err: Error, extra?: any) => {
    66      // Exceptions are such things as delivery failures. They do not put the connection in error state
    67      log.error(`App2app messaging exception. ${err.toString()}`, extra);
    68    }).on('data', (app2appMessage: IApp2AppMessage) => {
    69      log.trace(`App2App message ${JSON.stringify(app2appMessage)}`);
    70      try {
    71        const content: AssetTradeMessage = JSON.parse(app2appMessage.content);
    72        log.trace(`App2App message type=${content.type}`)
    73        for (const listener of listeners) {
    74          listener(app2appMessage.headers, content);
    75        }
    76      } catch (err) {
    77        log.error(`App2App message error ${err}`);
    78      } finally {
    79        socket.emit('commit');
    80      }
    81    }) as SocketIOClient.Socket;
    82  };
    83  
    84  export const addListener = (listener: IApp2AppMessageListener) => {
    85    listeners.push(listener);
    86  };
    87  
    88  export const removeListener = (listener: IApp2AppMessageListener) => {
    89    listeners = listeners.filter(entry => entry != listener);
    90  };
    91  
    92  export const dispatchMessage = (to: string, content: any) => {
    93    log.trace(`App2App dispatch type=${content.type}`)
    94    socket.emit('produce', {
    95      headers: {
    96        from: config.app2app.destinations.kat,
    97        to
    98      },
    99      content: JSON.stringify(content)
   100    }, 'kat', (err: any) => {
   101      if(err) {
   102        log.error(`Failed to dispatch App2App message.`, err);
   103      }
   104    });
   105  };
   106  
   107  export const reset = () => {
   108    if (socket) {
   109      log.info('App2App Socket IO connection reset');
   110      socket.close();
   111      establishSocketIOConnection();
   112    }
   113  };