[ https://issues.apache.org/jira/browse/SSHD-860?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16680201#comment-16680201 ]
Thomas Wolf edited comment on SSHD-860 at 11/8/18 7:10 PM: ----------------------------------------------------------- {quote}I am not sure I understand how this happens {quote} It means that somewhere along the line, something still uses a stream operation to obtain an iterator. An iterator on a stream will be such a spliterator, and those buffer. {quote}Can you indicate the code that uses these "newfangled spliterators" ? {quote} As far as I see, the culprit is this call chain: * UserAuthPublicKeyIterator.initializeSessionIdentities() * ClientSession.providerOf(session) * KeyIdentityProvider.resolveKeyIdentityProvider(session.getRegisteredIdentities(), session.getKeyPairProvider()) * KeyIdentityProvider.multiProvider(identities, keys) * KeyIdentityProvider.multiProvider(GenericUtils.asList(providers)) * KeyIdentityProvider.wrapKeyPairs(iterableOf(providers)) ** iterableOf(providers) calls *** GenericUtils.multiIterableSuppliers(GenericUtils.wrapIterable(providers, p -> p::loadKeys)) *** GenericUtils.wrapIterable(iter, mapper) returns {{() -> GenericUtils.wrapIterator(iter, mapper)}} **** GenericUtils.wrapIterator(iter, mapper) does {{return stream(iter).map(mapper).iterator()}} *** GenericUtils.multiIterableSuppliers(...) does {{return () -> stream(providers).flatMap(s -> stream(s.get())).map(Function.identity()).iterator()}} I'm not sure which of the last two is the real problem here; tracing this through debugging is highly confusing once it enters hasNext(). Looks to me we have in the end a spliterator over a stream of spliterators (a spliterator of spliterators). Which also explains why in my JGit code it doesn't occur; I made a point of not using any stream operations in these key iterators. was (Author: wolft): {quote}I am not sure I understand how this happens {quote} It means that somewhere along the line, something still uses a stream operation to obtain an iterator. An iterator on a stream will be such a spliterator, and those buffer. {quote}Can you indicate the code that uses these "newfangled spliterators" ? {quote} As far as I see, the culprit is this call chain: * UserAuthPublicKeyIterator.initializeSessionIdentities() * ClientSession.providerOf(session) * KeyIdentityProvider.resolveKeyIdentityProvider(session.getRegisteredIdentities(), session.getKeyPairProvider()) * KeyIdentityProvider.multiProvider(identities, keys) * KeyIdentityProvider.multiProvider(GenericUtils.asList(providers)) * KeyIdentityProvider.wrapKeyPairs(iterableOf(providers)) ** iterableOf(providers) calls *** GenericUtils.multiIterableSuppliers(GenericUtils.wrapIterable(providers, p -> p::loadKeys)), which returns {{() -> GenericUtils.wrapIterator(iter, mapper)}} **** GenericUtils.wrapIterator(iter, mapper) does {{return stream(iter).map(mapper).iterator()}} *** GenericUtils.multiIterableSuppliers(...) does {{return () -> stream(providers).flatMap(s -> stream(s.get())).map(Function.identity()).iterator()}} I'm not sure which of the last two is the real problem here; tracing this through debugging is highly confusing once it enters hasNext(). Looks to me we have in the end a spliterator over a stream of spliterators (a spliterator of spliterators). Which also explains why in my JGit code it doesn't occur; I made a point of not using any stream operations in these key iterators. > org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator pre-loads client > identities (private key files) > ------------------------------------------------------------------------------------------------------------ > > Key: SSHD-860 > URL: https://issues.apache.org/jira/browse/SSHD-860 > Project: MINA SSHD > Issue Type: Bug > Affects Versions: 2.0.0, 2.1.0, 2.1.1 > Reporter: Thomas Wolf > Assignee: Goldstein Lyor > Priority: Major > Fix For: 2.1.1 > > > {{org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}} loads and > caches in memory private keys prematurely. Keys are loaded even if they are > not used at all in the end. In other words, incremental loading of keys > doesn't work. > This is bad for two reasons: > # Private keys should be kept in memory only if and when needed. Loading > completely unused private keys must be avoided. > # With encrypted private keys, the user may end up being asked for > passphrases for keys that are not used at all in the end, which is highly > disruptive. > {{org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}} does in its > constructor: > {code:java} > Iterator<? extends PublicKeyIdentity> current; > Collection<Stream<? extends PublicKeyIdentity>> identities = new > LinkedList<>(); > ... > identities.add(Stream.of(ClientSession.providerOf(session)) > .map(KeyIdentityProvider::loadKeys) > .flatMap(GenericUtils::stream) > .map(kp -> new KeyPairIdentity(signatureFactories, session, kp))); > current = identities.stream().flatMap(r -> r).iterator(); > {code} > The final {{current}} iterator uses some internal buffer (size unknown; > didn't try to determine it) and will pre-fill this buffer completely. So with > buffer size _N_ it'll pre-load the first _N_ keys from the combined identity > stream. If the first key authenticates successfully, loading all the others > must not be done. > See my [test > case|https://github.com/tomaswolf/mina-sshd/blob/SSHD-860/sshd-core/src/test/java/org/apache/sshd/client/ClientKeyLoadTest.java] > showing this faulty behavior. It does exactly the same as the > {{UserAuthPublicKeyIterator}} constructor, using two iterators with two > identity files each, and then tests the resulting iterator. The first > {{hasNext()/next()}} call on the {{current}} iterator _loads all four keys!_ -- This message was sent by Atlassian JIRA (v7.6.3#76005)