Source:
<code>public void getTree(GetTreeRequest request, StreamObserver<GetTreeResponse> responseObserver) { Set<Digest> seenDigests = new HashSet<>(); Deque<Digest> pendingDigests = new ArrayDeque<>(); seenDigests.add(request.getRootDigest()); pendingDigests.push(request.getRootDigest()); boolean skipping = !request.getPageToken().isEmpty(); while (!pendingDigests.isEmpty() && !Context.current().isCancelled()) { Digest digest = pendingDigests.pop(); byte[] directoryBytes; try { directoryBytes = getFromFuture(cache.downloadBlob(digest)); } catch (CacheNotFoundException e) { responseObserver.onError(StatusUtils.notFoundError(digest)); return; } catch (Exception e) { logger.log(WARNING, "Read request failed.", e); responseObserver.onError(StatusUtils.internalError(e)); return; } Directory directory; try { directory = Directory.parseFrom(directoryBytes); } catch (InvalidProtocolBufferException e) { logger.log(WARNING, "Failed to parse directory in tree.", e); responseObserver.onError(StatusUtils.internalError(e)); return; } for (DirectoryNode directoryNode : directory.getDirectoriesList()) { if (!seenDigests.contains(directoryNode.getDigest())) { seenDigests.add(directoryNode.getDigest()); pendingDigests.push(directoryNode.getDigest()); } } GetTreeResponse response = GetTreeResponse.newBuilder() .addDirectories(directory) .setNextPageToken(pendingDigests.isEmpty() ? "" : digest.getHash()) .build(); if (!skipping) { responseObserver.onNext(response); } if (request.getPageToken().equals(digest.getHash())) { skipping = false; } } if (Context.current().isCancelled()) { <START> responseObserver.onError(Status.CANCELLED.asException()); <END> } else { responseObserver.onCompleted(); } }</code><technical_language>I check this explicitly. If context is cancelled server is interrupted, client get exception anyway; if request deadline exceeded. this handle</technical_language>

Target:
public void getTree(GetTreeRequest request, StreamObserver<GetTreeResponse> responseObserver) { Set<Digest> seenDigests = new HashSet<>(); Deque<Digest> pendingDigests = new ArrayDeque<>(); seenDigests.add(request.getRootDigest()); pendingDigests.push(request.getRootDigest()); GetTreeResponse.Builder responseBuilder = GetTreeResponse.newBuilder(); while (!pendingDigests.isEmpty()) { Digest digest = pendingDigests.pop(); byte[] directoryBytes; try { directoryBytes = getFromFuture(cache.downloadBlob(digest)); } catch (CacheNotFoundException e) { responseObserver.onError(StatusUtils.notFoundError(digest)); return; } catch (Exception e) { logger.log(WARNING, "Read request failed.", e); responseObserver.onError(StatusUtils.internalError(e)); return; } Directory directory; try { directory = Directory.parseFrom(directoryBytes); } catch (InvalidProtocolBufferException e) { logger.log(WARNING, "Failed to parse directory in tree.", e); responseObserver.onError(StatusUtils.internalError(e)); return; } responseBuilder.addDirectories(directory); for (DirectoryNode directoryNode : directory.getDirectoriesList()) { if (!seenDigests.contains(directoryNode.getDigest())) { seenDigests.add(directoryNode.getDigest()); pendingDigests.push(directoryNode.getDigest()); } } } responseObserver.onNext(responseBuilder.build()); responseObserver.onCompleted(); }