# All files / src/loader key-loader.ts

9.43% Statements 5/53
9.09% Branches 2/22
33.33% Functions 2/6
9.43% Lines 5/53

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136

    600x
    150x

    15x

    15x

    15x

     /*
     * Decrypt key Loader
    */

    import Event from '../events';
    import EventHandler from '../event-handler';
    import { ErrorTypes, ErrorDetails } from '../errors';
    import { logger } from '../utils/logger';
    import Hls from '../hls';
    import Fragment from './fragment';
    import { LoaderStats, LoaderResponse, LoaderContext, LoaderConfiguration, LoaderCallbacks } from '../types/loader';

    interface OnKeyLoadingPayload {
      frag: Fragment
    }

    interface KeyLoaderContext extends LoaderContext {
      frag: Fragment
    }

    class KeyLoader extends EventHandler {
      public loaders = {};
      public decryptkey: Uint8Array | null = null;
      public decrypturl: string | null = null;

      constructor (hls: Hls) {
        super(hls, Event.KEY_LOADING);
      }

      destroy (): void {
        for (const loaderName in this.loaders) {
          let loader = this.loaders[loaderName];
          if (loader) {
            loader.destroy();
          }
        }
        this.loaders = {};

        super.destroy();
      }

      onKeyLoading (data: OnKeyLoadingPayload) {
        const { frag } = data;
        const type = frag.type;
        const loader = this.loaders[type];
        if (!frag.decryptdata) {
          logger.warn('Missing decryption data on fragment in onKeyLoading');
          return;
        }

        // Load the key if the uri is different from previous one, or if the decrypt key has not yet been retrieved
        const uri = frag.decryptdata.uri;
        if (uri !== this.decrypturl || this.decryptkey === null) {
          let config = this.hls.config;
          if (loader) {
            logger.warn(`abort previous key loader for type:${type}`);
            loader.abort();
          }
          if (!uri) {
            logger.warn('key uri is falsy');
            return;
          }

          frag.loader = this.loaders[type] = new config.loader(config);
          this.decrypturl = uri;
          this.decryptkey = null;

          const loaderContext: KeyLoaderContext = {
            url: uri,
            frag: frag,
            responseType: 'arraybuffer'
          };

          // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times,
          // key-loader will trigger an error and rely on stream-controller to handle retry logic.
          // this will also align retry logic with fragment-loader
          const loaderConfig: LoaderConfiguration = {
            timeout: config.fragLoadingTimeOut,
            maxRetry: 0,
            retryDelay: config.fragLoadingRetryDelay,
            maxRetryDelay: config.fragLoadingMaxRetryTimeout
          };

          const loaderCallbacks: LoaderCallbacks<KeyLoaderContext> = {
            onSuccess: this.loadsuccess.bind(this),
            onError: this.loaderror.bind(this),
            onTimeout: this.loadtimeout.bind(this)
          };

          frag.loader.load(loaderContext, loaderConfig, loaderCallbacks);
        } else if (this.decryptkey) {
          // Return the key if it's already been loaded
          frag.decryptdata.key = this.decryptkey;
          this.hls.trigger(Event.KEY_LOADED, { frag: frag });
        }
      }

      loadsuccess (response: LoaderResponse, stats: LoaderStats, context: KeyLoaderContext) {
        let frag = context.frag;
        if (!frag.decryptdata) {
          logger.error('after key load, decryptdata unset');
          return;
        }
        this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data as ArrayBuffer);

        // detach fragment loader on load success
        frag.loader = undefined;
        delete this.loaders[frag.type];
        this.hls.trigger(Event.KEY_LOADED, { frag: frag });
      }

      loaderror (response: LoaderResponse, context: KeyLoaderContext) {
        let frag = context.frag;
        let loader = frag.loader;
        if (loader) {
          loader.abort();
        }

        delete this.loaders[frag.type];
        this.hls.trigger(Event.ERROR, { type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag, response });
      }

      loadtimeout (stats: LoaderStats, context: KeyLoaderContext) {
        let frag = context.frag;
        let loader = frag.loader;
        if (loader) {
          loader.abort();
        }

        delete this.loaders[frag.type];
        this.hls.trigger(Event.ERROR, { type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag });
      }
    }

    export default KeyLoader;