コード例 #1
0
        /// <summary>
        /// Copies file to stream
        /// </summary>
        public async Task <CopyFileResult> CopyToAsync(Context context, ContentHash contentHash, Stream stream, CancellationToken cancellationToken, long fileSize = -1, bool enableCompression = false)
        {
            // TODO: Pipe through flag for compression type
            CopyCompression compression   = enableCompression ? CopyCompression.Gzip : CopyCompression.None;
            long            bytesReceived = 0L;

            try
            {
                AsyncServerStreamingCall <CopyFileResponse> response = _client.CopyFile(new CopyFileRequest
                {
                    TraceId     = context.Id.ToString(),
                    HashType    = (int)contentHash.HashType,
                    ContentHash = contentHash.ToByteString(),
                    // TODO: If `Drive` is expected to be the drive of the file on the source machine, then this should have nothing to do with the destination's drive
                    Drive       = "B",
                    Offset      = 0,
                    Compression = compression
                });

                bytesReceived = await StreamContentAsync(stream, response.ResponseStream);
            }
            catch (RpcException r) when(r.StatusCode == StatusCode.Unavailable && r.Message.Contains("Connect Failed"))
            {
                return(new CopyFileResult(CopyFileResult.ResultCode.SourcePathError, r, $"Failed to connect to server {_host} at port {_grpcPort}"));
            }

            if (bytesReceived == 0)
            {
                return(new CopyFileResult(CopyFileResult.ResultCode.SourcePathError, $"Received {bytesReceived} bytes for {contentHash}. Source file does not exist"));
            }
            else if (fileSize >= 0 && bytesReceived != fileSize)
            {
                return(new CopyFileResult(CopyFileResult.ResultCode.InvalidHash, $"Received {bytesReceived} bytes for {contentHash}, expected {fileSize}"));
            }

            return(CopyFileResult.SuccessWithSize(bytesReceived));
        }
コード例 #2
0
        /// <summary>
        /// Copies file to stream
        /// </summary>
        public async Task <CopyFileResult> CopyFileAsync(Context context, ContentHash contentHash, AbsolutePath destinationPath, long fileSize = -1, bool enableCompression = false)
        {
            // TODO: Pipe through flag for compression type
            CopyCompression compression = enableCompression ? CopyCompression.Gzip : CopyCompression.None;
            AsyncServerStreamingCall <CopyFileResponse> response = _client.CopyFile(new CopyFileRequest()
            {
                TraceId     = context.Id.ToString(),
                HashType    = (int)contentHash.HashType,
                ContentHash = contentHash.ToByteString(),
                Drive       = destinationPath.DriveLetter.ToString(),
                Offset      = 0,
                Compression = compression
            });

            IAsyncStreamReader <CopyFileResponse> replyStream = response.ResponseStream;
            long bytesReceived = 0L;

            using (var stream = new
                                FileStream(destinationPath.Path, FileMode.Create, FileAccess.Write, FileShare.None, ContentStore.Grpc.Utils.DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan))
            {
                bytesReceived = await StreamContentAsync(stream, response.ResponseStream);

                while (await replyStream.MoveNext(CancellationToken.None))
                {
                    CopyFileResponse oneOfManyReply = replyStream.Current;
                    bytesReceived += oneOfManyReply.Content.Length;
                    oneOfManyReply.Content.WriteTo(stream);
                }
            }

            if (fileSize >= 0 && bytesReceived != fileSize)
            {
                return(new CopyFileResult(CopyFileResult.ResultCode.InvalidHash, $"Received {bytesReceived} bytes for {contentHash}, expected {fileSize}"));
            }
            return(CopyFileResult.Success);
        }
コード例 #3
0
ファイル: GrpcCopyClient.cs プロジェクト: uilit/BuildXL
        /// <summary>
        /// Copies content from the server to the stream returned by the factory.
        /// </summary>
        private async Task <CopyFileResult> CopyToCoreAsync(OperationContext context, ContentHash hash, Func <Stream> streamFactory)
        {
            try
            {
                CopyFileRequest request = new CopyFileRequest()
                {
                    TraceId     = context.TracingContext.Id.ToString(),
                    HashType    = (int)hash.HashType,
                    ContentHash = hash.ToByteString(),
                    Offset      = 0,
                    Compression = Key.UseCompression ? CopyCompression.Gzip : CopyCompression.None
                };

                AsyncServerStreamingCall <CopyFileResponse> response = _client.CopyFile(request);

                Metadata headers = await response.ResponseHeadersAsync;

                // If the remote machine couldn't be contacted, GRPC returns an empty
                // header collection. GRPC would throw an RpcException when we tried
                // to stream response, but by that time we would have created target
                // stream. To avoid that, exit early instead.
                if (headers.Count == 0)
                {
                    return(new CopyFileResult(CopyFileResult.ResultCode.SourcePathError, $"Failed to connect to copy server {Key.Host} at port {Key.GrpcPort}."));
                }

                // Parse header collection.
                string          exception   = null;
                string          message     = null;
                CopyCompression compression = CopyCompression.None;
                foreach (Metadata.Entry header in headers)
                {
                    switch (header.Key)
                    {
                    case "exception":
                        exception = header.Value;
                        break;

                    case "message":
                        message = header.Value;
                        break;

                    case "compression":
                        Enum.TryParse(header.Value, out compression);
                        break;
                    }
                }

                // Process reported server-side errors.
                if (exception != null)
                {
                    Contract.Assert(message != null);
                    switch (exception)
                    {
                    case "ContentNotFound":
                        return(new CopyFileResult(CopyFileResult.ResultCode.FileNotFoundError, message));

                    default:
                        return(new CopyFileResult(CopyFileResult.ResultCode.SourcePathError, message));
                    }
                }

                // We got headers back with no errors, so create the target stream.
                Stream targetStream;
                try
                {
                    targetStream = streamFactory();
                }
                catch (Exception targetException)
                {
                    return(new CopyFileResult(CopyFileResult.ResultCode.DestinationPathError, targetException));
                }

                // Copy the content to the target stream.
                using (targetStream)
                {
                    switch (compression)
                    {
                    case CopyCompression.None:
                        await StreamContentAsync(targetStream, response.ResponseStream, context.Token);

                        break;

                    case CopyCompression.Gzip:
                        await StreamContentWithCompressionAsync(targetStream, response.ResponseStream, context.Token);

                        break;

                    default:
                        throw new NotSupportedException($"CopyCompression {compression} is not supported.");
                    }
                }

                return(CopyFileResult.Success);
            }
            catch (RpcException r)
            {
                if (r.StatusCode == StatusCode.Unavailable)
                {
                    return(new CopyFileResult(CopyFileResult.ResultCode.SourcePathError, r));
                }
                else
                {
                    return(new CopyFileResult(CopyFileResult.ResultCode.Unknown, r));
                }
            }
        }
コード例 #4
0
        private async Task <CopyFileResult> CopyToCoreAsync(OperationContext context, ContentHash hash, CopyOptions options, Func <Stream> streamFactory, bool closeStream)
        {
            using var cts = CancellationTokenSource.CreateLinkedTokenSource(context.Token);
            var            token              = cts.Token;
            bool           exceptionThrown    = false;
            TimeSpan?      headerResponseTime = null;
            CopyFileResult?result             = null;

            try
            {
                CopyFileRequest request = new CopyFileRequest()
                {
                    TraceId        = context.TracingContext.Id.ToString(),
                    HashType       = (int)hash.HashType,
                    ContentHash    = hash.ToByteString(),
                    Offset         = 0,
                    Compression    = _configuration.UseGzipCompression ? CopyCompression.Gzip : CopyCompression.None,
                    FailFastIfBusy = options.BandwidthConfiguration?.FailFastIfServerIsBusy ?? false,
                };

                using AsyncServerStreamingCall <CopyFileResponse> response = _client.CopyFile(request, options: GetDefaultGrpcOptions(token));
                Metadata headers;
                var      stopwatch = StopwatchSlim.Start();
                try
                {
                    var connectionTimeout = GetResponseHeadersTimeout(options);
                    headers = await response.ResponseHeadersAsync.WithTimeoutAsync(connectionTimeout, token);

                    headerResponseTime = stopwatch.Elapsed;
                }
                catch (TimeoutException t)
                {
                    // Trying to cancel the back end operation as well.
                    cts.Cancel();
                    result = new CopyFileResult(GetCopyResultCodeForGetResponseHeaderTimeout(), t);
                    return(result);
                }

                // If the remote machine couldn't be contacted, GRPC returns an empty
                // header collection. GRPC would throw an RpcException when we tried
                // to stream response, but by that time we would have created target
                // stream. To avoid that, exit early instead.
                if (headers.Count == 0)
                {
                    result = new CopyFileResult(
                        CopyResultCode.ServerUnavailable,
                        $"Failed to connect to copy server {Key.Host} at port {Key.GrpcPort}.");
                    return(result);
                }

                // Parse header collection.
                string?         exception   = null;
                string?         message     = null;
                CopyCompression compression = CopyCompression.None;
                foreach (Metadata.Entry header in headers)
                {
                    switch (header.Key)
                    {
                    case "exception":
                        exception = header.Value;
                        break;

                    case "message":
                        message = header.Value;
                        break;

                    case "compression":
                        Enum.TryParse(header.Value, out compression);
                        break;
                    }
                }

                // Process reported server-side errors.
                if (exception != null)
                {
                    Contract.Assert(message != null);
                    switch (exception)
                    {
                    case "ContentNotFound":
                        result = new CopyFileResult(CopyResultCode.FileNotFoundError, message);
                        return(result);

                    default:
                        result = new CopyFileResult(CopyResultCode.UnknownServerError, message);
                        return(result);
                    }
                }

                // We got headers back with no errors, so create the target stream.
                Stream targetStream;
                try
                {
                    targetStream = streamFactory();
                }
                catch (Exception targetException)
                {
                    result = new CopyFileResult(CopyResultCode.DestinationPathError, targetException);
                    return(result);
                }

                result = await _bandwidthChecker.CheckBandwidthAtIntervalAsync(
                    context,
                    innerToken => copyToCoreImplementation(response, compression, targetStream, innerToken),
                    options,
                    getErrorResult : diagnostics => new CopyFileResult(CopyResultCode.CopyBandwidthTimeoutError, diagnostics));

                return(result);
            }
            catch (RpcException r)
            {
                result = CreateResultFromException(r);
                return(result);
            }
            catch (Exception)
            {
                exceptionThrown = true;
                throw;
            }
            finally
            {
                // Even though we don't expect exceptions in this method, we can't assume they won't happen.
                // So asserting that the result is not null only when the method completes successfully or with a known errors.
                Contract.Assert(exceptionThrown || result != null);
                if (result != null)
                {
                    result.HeaderResponseTime = headerResponseTime;
                }
            }

            async Task <CopyFileResult> copyToCoreImplementation(AsyncServerStreamingCall <CopyFileResponse> response, CopyCompression compression, Stream targetStream, CancellationToken token)
            {
                // Copy the content to the target stream.
                try
                {
                    switch (compression)
                    {
                    case CopyCompression.None:
                        await StreamContentAsync(response.ResponseStream, targetStream, options, token);

                        break;

                    case CopyCompression.Gzip:
                        await StreamContentWithCompressionAsync(response.ResponseStream, targetStream, options, token);

                        break;

                    default:
                        throw new NotSupportedException($"CopyCompression {compression} is not supported.");
                    }
                }
                finally
                {
                    if (closeStream)
                    {
#pragma warning disable AsyncFixer02 // A disposable object used in a fire & forget async call
                        targetStream.Dispose();
#pragma warning restore AsyncFixer02 // A disposable object used in a fire & forget async call
                    }
                }

                return(CopyFileResult.Success);
            }
        }