// Accepting a stream factory instead of a stream allows the file // to be created only when content is actually available. public async Task <CopyResult> Read(string name, Func <Stream> streamFactory, CancellationToken ct = default(CancellationToken)) { if (name is null) { throw new ArgumentNullException(nameof(name)); } if (streamFactory is null) { throw new ArgumentNullException(nameof(streamFactory)); } // Create a request object ReadRequest request = new ReadRequest() { FileName = name, Offset = 0, Compression = CopyCompression.None //Compression = CopyCompression.Gzip }; // Create a result object CopyResult result = new CopyResult() { FileName = name, Status = CopyResultStatus.Successful }; try { // Create a CancellationTokenSource we will use to cancel on time-outs // and to transmit any cancellation to server. using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct)) { CallOptions options = default(CallOptions).WithCancellationToken(cts.Token); // Get the initial response Metadata responseHeaders; Stopwatch responseTimer = Stopwatch.StartNew(); AsyncServerStreamingCall <Chunk> reply; try { reply = client.Read(request, options); responseHeaders = await reply.ResponseHeadersAsync.TimeoutAfter(ResponseTimeout, cts).ConfigureAwait(false); } catch (Exception e) { result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; if (e is TimeoutException) { result.Status = CopyResultStatus.ConnectionTimeout; } else { result.Status = CopyResultStatus.ConnectionFailure; } return(result); } finally { responseTimer.Stop(); result.ResponseTime = responseTimer.Elapsed; } // Irritatingly, if server does not respond, we get // no error on call or reading response headers, // but instead exception when we start to read stream. // We can avoid this by exiting early if we get no // response headers. if (responseHeaders.Count == 0) { result.Status = CopyResultStatus.ConnectionFailure; return(result); } // Extract response header data ReadResponse response = ReadResponse.FromHeaders(responseHeaders); if (response.ErrorType is object) { result.ErrorType = response.ErrorType; result.ErrorMessage = response.ErrorMessage; if (response.ErrorType == typeof(FileNotFoundException).Name) { result.Status = CopyResultStatus.FileNotFound; } else if (response.ErrorType == typeof(ThrottledException).Name) { result.Status = CopyResultStatus.RequestThrottled; } else { result.Status = CopyResultStatus.FileAccessErrorOnServer; } return(result); } result.Compression = response.Compression; long headerSize = response.FileSize; if (result.Status != CopyResultStatus.Successful) { return(result); } // We got a successful response, so get a stream to write to Stream writeStream; try { writeStream = streamFactory(); } catch (Exception e) { result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; result.Status = CopyResultStatus.FileAccessErrorOnClient; return(result); } // Stream the content long measuredSize = 0L; using (writeStream) { // Prepare a content streamer and a bandwidth monitor that uses it //(GrpcContentStreamer streamer, Func<string> checker) = GetStreamerAndChecker(); GrpcContentStreamer streamer = new GrpcContentStreamer(govener); // Stream the bytes Stopwatch streamTimer = Stopwatch.StartNew(); try { switch (result.Compression) { case CopyCompression.None: await streamer.ReadContent(writeStream, reply.ResponseStream, cts.Token).ConfigureAwait(false); break; case CopyCompression.Gzip: throw new NotImplementedException(); //(chunks, bytes) = await ReadContentWithCompression(writeStream, reply.ResponseStream, cts.Token).TimeoutOnCondition(bandwidthCheckInterval, bandwidthCheck, cts).ConfigureAwait(false); } } catch (Exception e) { result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; if (e is TimeoutException) { result.Status = CopyResultStatus.StreamingTimeout; } else { result.Status = CopyResultStatus.StreamingFailure; } return(result); } finally { streamTimer.Stop(); result.BytesStreamed = streamer.Bytes; result.StreamingTime = streamTimer.Elapsed; } govener.Record(streamer.Bytes, streamTimer.Elapsed); measuredSize = writeStream.Length; Console.WriteLine($"responseTime = {result.ResponseTime} chunks = {streamer.Chunks} bytes = {streamer.Bytes} headerSize = {headerSize} measuredSize = {measuredSize} streamTime = {result.StreamingTime}"); } return(result); } } catch (Exception e) { result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; result.Status = CopyResultStatus.OtherClientSideError; return(result); } }
public async Task <CopyResult> Write(Stream stream, string name = null, CancellationToken ct = default(CancellationToken)) { if (stream is null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanRead) { throw new InvalidOperationException(); } WriteRequest request = new WriteRequest() { FileName = name }; CopyResult result = new CopyResult() { FileName = name }; try { Metadata requestHeaders = request.ToMetadata(); // Create a CancellationTokenSource we will use to cancel on time-outs // and to transmit any cancellation to server. using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct)) { CallOptions options = default(CallOptions).WithHeaders(requestHeaders).WithCancellationToken(cts.Token); AsyncClientStreamingCall <Chunk, WriteReply> call; Stopwatch responseTimer = Stopwatch.StartNew(); try { call = client.Write(options); //responseHeaders = await call.ResponseHeadersAsync.ConfigureAwait(false); } catch (Exception e) { // TODO: Also handle timeout result.Status = CopyResultStatus.ConnectionFailure; result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; return(result); } finally { responseTimer.Stop(); result.ResponseTime = responseTimer.Elapsed; } byte[] buffer = new byte[CopyConstants.BufferSize]; GrpcContentStreamer streamer = new GrpcContentStreamer(govener); Stopwatch streamingTimer = Stopwatch.StartNew(); try { await streamer.WriteContent(stream, buffer, call.RequestStream, ct).ConfigureAwait(false); } catch (Exception e) { result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; if (e is TimeoutException) { result.Status = CopyResultStatus.StreamingTimeout; } else { result.Status = CopyResultStatus.StreamingFailure; } return(result); } finally { streamingTimer.Stop(); result.BytesStreamed = streamer.Bytes; result.StreamingTime = streamingTimer.Elapsed; } // gRPC requires client to explicitly declare end-of-stream await call.RequestStream.CompleteAsync().ConfigureAwait(false); WriteReply reply = await call.ResponseAsync; result.FileName = reply.FileName; } return(result); } catch (Exception e) { result.Status = CopyResultStatus.OtherClientSideError; result.ErrorType = e.GetType().Name; result.ErrorMessage = e.Message; return(result); } }