def list_objects(self, path='', relative=False, first_level=False,
                     max_request_entries=None):
        """
        List objects.

        Args:
            path (str): Path or URL.
            relative (bool): Path is relative to current root.
            first_level (bool): It True, returns only first level objects.
                Else, returns full tree.
            max_request_entries (int): If specified, maximum entries returned
                by request.

        Returns:
            generator of tuple: object name str, object header dict
        """
        entries = 0
        max_request_entries_arg = None

        if not relative:
            path = self.relpath(path)

        # From root
        if not path:
            locators = self._list_locators()

            # Yields locators
            if first_level:
                for locator in locators:

                    entries += 1
                    yield locator
                    if entries == max_request_entries:
                        return
                return

            # Yields each locator objects
            for loc_path, loc_header in locators:

                # Yields locator itself
                loc_path = loc_path.strip('/')

                entries += 1
                yield loc_path, loc_header
                if entries == max_request_entries:
                    return

                # Yields locator content is read access to it
                if max_request_entries is not None:
                    max_request_entries_arg = max_request_entries - entries
                try:
                    for obj_path, obj_header in self._list_objects(
                            self.get_client_kwargs(loc_path), '',
                            max_request_entries_arg):

                        entries += 1
                        yield ('/'.join((loc_path, obj_path.lstrip('/'))),
                               obj_header)
                        if entries == max_request_entries:
                            return

                except ObjectPermissionError:
                    # No read access to locator
                    continue
            return

        # From locator or sub directory
        locator, path = self.split_locator(path)

        if first_level:
            seen = set()

        if max_request_entries is not None:
            max_request_entries_arg = max_request_entries - entries

        for obj_path, header in self._list_objects(
                self.get_client_kwargs(locator), path, max_request_entries_arg):

            if path:
                try:
                    obj_path = obj_path.split(path, 1)[1]
                except IndexError:
                    # Not sub path of path
                    continue
            obj_path = obj_path.lstrip('/')

            # Skips parent directory
            if not obj_path:
                continue

            # Yields first level locator objects only
            if first_level:
                # Directory
                try:
                    obj_path, _ = obj_path.strip('/').split('/', 1)
                    obj_path += '/'

                    # Avoids to use the header of the object instead of the
                    # non existing header of the directory that only exists
                    # virtually in object path.
                    header = dict()

                # File
                except ValueError:
                    pass

                if obj_path not in seen:
                    entries += 1
                    yield obj_path, header
                    if entries == max_request_entries:
                        return
                    seen.add(obj_path)

            # Yields locator objects
            else:
                entries += 1
                yield obj_path, header
                if entries == max_request_entries:
                    return
List objects.

        Args:
            path (str): Path or URL.
            relative (bool): Path is relative to current root.
            first_level (bool): It True, returns only first level objects.
                Else, returns full tree.
            max_request_entries (int): If specified, maximum entries returned
                by request.

        Returns:
            generator of tuple: object name str, object header dict