docs: def add_handler(app, config):
      """Add a single handler to the app.
  
      Adds one IIIF Image API handler to app, with config from config.
  
      Arguments:
          app - Flask app
          config - Configuration object in which:
              config.prefix - String path prefix for this handler
              config.client_prefix - String path prefix seen by client (which may be different
                  because of reverse proxy or such
              config.klass_name - Manipulator class, e.g. 'pil'
              config.api_version - e.g. '2.1'
              config.include_osd - True or False to include OSD
              config.gauth_client_secret_file - filename if auth_type='gauth'
              config.access_cookie_lifetime - number of seconds
              config.access_token_lifetime - number of seconds
              config.auth_type - Auth type string or 'none'
  
      Returns True on success, nothing otherwise.
      """
      auth = None
      if (config.auth_type is None or config.auth_type == 'none'):
          pass
      elif (config.auth_type == 'gauth'):
          from iiif.auth_google import IIIFAuthGoogle
          auth = IIIFAuthGoogle(client_secret_file=config.gauth_client_secret_file)
      elif (config.auth_type == 'basic'):
          from iiif.auth_basic import IIIFAuthBasic
          auth = IIIFAuthBasic()
      elif (config.auth_type == 'clickthrough'):
          from iiif.auth_clickthrough import IIIFAuthClickthrough
          auth = IIIFAuthClickthrough()
      elif (config.auth_type == 'kiosk'):
          from iiif.auth_kiosk import IIIFAuthKiosk
          auth = IIIFAuthKiosk()
      elif (config.auth_type == 'external'):
          from iiif.auth_external import IIIFAuthExternal
          auth = IIIFAuthExternal()
      else:
          logging.error("Unknown auth type %s, ignoring" % (config.auth_type))
          return
      if (auth is not None):
          auth.access_cookie_lifetime = config.access_cookie_lifetime
          auth.access_token_lifetime = config.access_token_lifetime
      klass = None
      if (config.klass_name == 'pil'):
          from iiif.manipulator_pil import IIIFManipulatorPIL
          klass = IIIFManipulatorPIL
      elif (config.klass_name == 'netpbm'):
          from iiif.manipulator_netpbm import IIIFManipulatorNetpbm
          klass = IIIFManipulatorNetpbm
      elif (config.klass_name == 'dummy'):
          from iiif.manipulator import IIIFManipulator
          klass = IIIFManipulator
      elif (config.klass_name == 'gen'):
          from iiif.manipulator_gen import IIIFManipulatorGen
          klass = IIIFManipulatorGen
      else:
          logging.error("Unknown manipulator type %s, ignoring" % (config.klass_name))
          return
      base = urljoin('/', config.prefix + '/')  # ensure has trailing slash
      client_base = urljoin('/', config.client_prefix + '/')  # ensure has trailing slash
      logging.warning("Installing %s IIIFManipulator at %s v%s %s" %
                      (config.klass_name, base, config.api_version, config.auth_type))
      params = dict(config=config, klass=klass, auth=auth, prefix=config.client_prefix)
      app.add_url_rule(base.rstrip('/'), 'prefix_index_page',
                       prefix_index_page, defaults={'config': config})
      app.add_url_rule(base, 'prefix_index_page',
                       prefix_index_page, defaults={'config': config})
      app.add_url_rule(base + '<string(minlength=1):identifier>/info.json',
                       'options_handler', options_handler, methods=['OPTIONS'])
      app.add_url_rule(base + '<string(minlength=1):identifier>/info.json',
                       'iiif_info_handler', iiif_info_handler, methods=['GET'], defaults=params)
      if (config.include_osd):
          app.add_url_rule(base + '<string(minlength=1):identifier>/osd.html',
                           'osd_page_handler', osd_page_handler, methods=['GET'], defaults=params)
      app.add_url_rule(base + '<string(minlength=1):identifier>/<path:path>',
                       'iiif_image_handler', iiif_image_handler, methods=['GET'], defaults=params)
      if (auth):
          setup_auth_paths(app, auth, config.client_prefix, params)
      # redirects to info.json must come after auth
      app.add_url_rule(base + '<string(minlength=1):identifier>',
                       'iiif_info_handler',
                       redirect_to=client_base + '<identifier>/info.json')
      app.add_url_rule(base + '<string(minlength=1):identifier>/',
                       'iiif_info_handler',
                       redirect_to=client_base + '<identifier>/info.json')
      return True