Skip to content

Commit 5e7db24

Browse files
committed
fix: handle timeout and concurrent stream reads in runProcessGetFullOutput
1 parent c603237 commit 5e7db24

1 file changed

Lines changed: 33 additions & 20 deletions

File tree

src/main/java/io/github/guacsec/trustifyda/tools/Operations.java

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.util.List;
3030
import java.util.Map;
3131
import java.util.Optional;
32+
import java.util.concurrent.CompletableFuture;
33+
import java.util.concurrent.ExecutionException;
3234
import java.util.concurrent.TimeUnit;
3335
import java.util.logging.Logger;
3436
import java.util.stream.Collectors;
@@ -210,34 +212,45 @@ public static ProcessExecOutput runProcessGetFullOutput(
210212
}
211213
}
212214

213-
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
214-
StringBuilder output = new StringBuilder();
215-
String line;
216-
while ((line = reader.readLine()) != null) {
217-
output.append(line);
218-
if (!line.endsWith(System.lineSeparator())) {
219-
output.append("\n");
220-
}
221-
}
215+
CompletableFuture<String> stdoutFuture =
216+
CompletableFuture.supplyAsync(() -> drainStream(process.getInputStream()));
217+
CompletableFuture<String> stderrFuture =
218+
CompletableFuture.supplyAsync(() -> drainStream(process.getErrorStream()));
222219

223-
reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
224-
StringBuilder error = new StringBuilder();
225-
while ((line = reader.readLine()) != null) {
226-
error.append(line);
227-
if (!line.endsWith(System.lineSeparator())) {
228-
error.append("\n");
229-
}
220+
boolean finished = process.waitFor(30L, TimeUnit.SECONDS);
221+
if (!finished) {
222+
process.destroyForcibly();
223+
throw new RuntimeException(
224+
String.format("Command '%s' timed out after 30 seconds", join(" ", cmdList)));
230225
}
231226

232-
process.waitFor(30L, TimeUnit.SECONDS);
233-
234-
return new ProcessExecOutput(output.toString(), error.toString(), process.exitValue());
235-
} catch (IOException | InterruptedException e) {
227+
return new ProcessExecOutput(stdoutFuture.get(), stderrFuture.get(), process.exitValue());
228+
} catch (InterruptedException e) {
229+
Thread.currentThread().interrupt();
230+
throw new RuntimeException(
231+
String.format("Command '%s' was interrupted", join(" ", cmdList)), e);
232+
} catch (ExecutionException e) {
233+
throw new RuntimeException(
234+
String.format("Failed reading output of command '%s'", join(" ", cmdList)), e);
235+
} catch (IOException e) {
236236
throw new RuntimeException(
237237
String.format("Failed to execute command '%s' ", join(" ", cmdList)), e);
238238
}
239239
}
240240

241+
private static String drainStream(java.io.InputStream inputStream) {
242+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
243+
StringBuilder sb = new StringBuilder();
244+
String line;
245+
while ((line = reader.readLine()) != null) {
246+
sb.append(line).append("\n");
247+
}
248+
return sb.toString();
249+
} catch (IOException e) {
250+
throw new RuntimeException("Failed to read process stream", e);
251+
}
252+
}
253+
241254
public static class ProcessExecOutput {
242255
private final String output;
243256
private final String error;

0 commit comments

Comments
 (0)