Nvious1
Nvious1

Reputation: 43

grpc-dotnet server streaming of local process output

I am working with grpc-dotnet in 3.1 for a project. On the server side, the grpc call will be executing a local system process (such as binaries or scripts). The issue I am having is the streaming of the redirected output from the process back over the grpc client. All I am getting right now is the first line of output. Here is my testing code.

public override async Task ExecuteProcess(ProcessRequest request, IServerStreamWriter<ProcessReply> responseStream, ServerCallContext context)
    {
        _logger.LogInformation($"Received Execution request for {request.Name}");
        _logger.LogInformation($"Received Args {request.Args}");
        using (var process = new Process())
        {
            process.StartInfo.FileName = @"cmd.exe";
            process.StartInfo.Arguments = @"/c dir";      // print the current working directory information
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            //process.OutputDataReceived += async(sender, e) => await responseStream.WriteAsync(new ProcessReply { Message = e.Data });
            process.OutputDataReceived += (sender, e) => ShowOutput(e.Data, responseStream)
            process.Start();
            process.BeginOutputReadLine();
            //process.BeginErrorReadLine();
            var exited = process.WaitForExit(1000 * 10);     // (optional) wait up to 10 second
        }
        //return Task.FromResult(new ProcessReply { Message = "Executed " + request.Name });
        await responseStream.WriteAsync(new ProcessReply
        {
            Message = "Process Complete"
        });
    }

    private async Task ShowOutput(string data, IServerStreamWriter<ProcessReply> responseStream)
    {
        if (data != null)
        {
            _logger.LogInformation($"Process Output {data}");
            await responseStream.WriteAsync(new ProcessReply { Message = data });

        }
    }

On the client side, I am just using Streaming Server example code to read the lines from the stream.

await foreach (var message in call.ResponseStream.ReadAllAsync())
{
     Console.WriteLine("Server Out: " + message.Message);
}

Any ideas why I am only getting 1 line of the process output from the redirected process output. I am only seeing the the one line in the logger output as well.

Upvotes: 0

Views: 538

Answers (1)

Mark Rendle
Mark Rendle

Reputation: 9414

This might be because you're writing to the ResponseStream from an event handler, which is not necessarily being called on the right async context for the stream.

Take a look at CliWrap which wraps the whole process execution thing in a very nice API, including a ListenAsync method which returns an IAsyncEnumerable that you can await foreach over and write the output to the stream.

Borrowing from the README, it would look something like this:

var cmd = Cli.Wrap("cmd.exe").WithArguments("/c dir");

await foreach (var cmdEvent in cmd.ListenAsync())
{
    switch (cmdEvent)
    {
        case StandardOutputCommandEvent stdOut:
            await responseStream.WriteAsync(new ProcessReply { Message = stdOut.Text });
            break;
    }
}

await responseStream.WriteAsync(new ProcessReply { Message = "Process Complete" });

Upvotes: 1

Related Questions