示例#1
0
        public async Task <IActionResult> Get(CancellationToken cancellationToken, ushort major, byte minor, byte patch,
                                              string pdbName, Guid pdbId, uint pdbAge)
        {
            SemanticVersion version = new SemanticVersion(major, minor, patch);

            if (version <= SymCacheVersion.MinVersion)
            {
                return(NotFound());
            }

            if (version.Major < transcoderVersion.Major)
            {
                // The client is using an older major version than the server's transcoder, so it wouldn't understand
                // the format of any SymCache files this server would return, because they have breaking changes
                // compared to the format the client understands.
                return(NotFound());
            }

            SemanticVersion ifVersionExceedsVersion;
            string          errorMessage;

            if (!TryParseIfVersionExceedsHeader(version, out ifVersionExceedsVersion, out errorMessage))
            {
                return(BadRequest(errorMessage));
            }

            SymCacheKey key = new SymCacheKey(version, pdbName, pdbId, pdbAge);

            CacheResult cacheResult = repository.Find(key);

            if (cacheResult.Status == CacheStatus.PositivelyCached)
            {
                if (ifVersionExceedsVersion != null && ifVersionExceedsVersion >= cacheResult.Version)
                {
                    // The client wants only a newer version than this server can offer; don't send the server's file.
                    return(NotModified());
                }

                return(Success(cacheResult.Path, cacheResult.Version));
            }
            else if (cacheResult.Status == CacheStatus.NegativelyCached)
            {
                return(NotFound());
            }
            else
            {
                Debug.Assert(cacheResult.Status == CacheStatus.NotCached);
            }

            if (ifVersionExceedsVersion != null && ifVersionExceedsVersion >= transcoderVersion)
            {
                // The client wants only a newer version than this server could transcoder will produce. Don't try to
                // transcode.
                return(NotModified());
            }

            if (ShouldTranscodeAsynchronously(version))
            {
                // Queue the item to be transcoded on a background thread.
                transcodeQueue.Enqueue(key);
                // Ask the client to check back again in 1 second to see if we have a result available. (It is up to
                // the client to decide how many times to retry, and whether to use the suggested delay, or have
                // some static value it always uses, or use the suggested value within a min/max range).
                return(NotFoundRetryAfter(TimeSpan.FromSeconds(1)));
            }

            // Older clients expected the symcache file transcoded before sending a response to the HTTP GET request.
            string transcodedPath = await transcoder.TryTranscodeAsync(key, cancellationToken);

            if (transcodedPath == null)
            {
                repository.MarkNegativelyCached(key);
                return(NotFound());
            }

            return(Success(transcodedPath, transcoderVersion));
        }
    public async Task <string> TryTranscodeAsync(SymCacheKey key, CancellationToken cancellationToken)
    {
        // Check whether a result already exists. If so, don't try to transcode again.
        CacheResult result = repository.Find(key);

        if (result.Status == CacheStatus.NegativelyCached)
        {
            return(null);
        }
        else if (result.Status == CacheStatus.PositivelyCached)
        {
            return(result.Path);
        }

        Debug.Assert(result.Status == CacheStatus.NotCached);

        string pdbPath = await symbolServer.TryGetPdbPathAsync(key.PdbName, key.PdbId, key.PdbAge, cancellationToken);

        if (pdbPath == null)
        {
            repository.MarkNegativelyCached(key);
            return(null);
        }

        // Use a separate, random directory under the temp directory so that concurrent transcodes do not collide.
        using (TempDirectory randomDirectory = new TempDirectory(Path.Combine(tempDirectory,
                                                                              Guid.NewGuid().ToString())))
        {
            string pdbDirectory = Path.Combine(randomDirectory.FullName, "pdb");

            Directory.CreateDirectory(pdbDirectory);

            // Cache the PDB from the sybol server locally, so transcoding (which is expensive) is accessing a local
            // file.
            string localPdbPath = Path.Combine(pdbDirectory, Path.GetFileName(pdbPath));

            try
            {
                File.Copy(pdbPath, localPdbPath);
            }
            catch
            {
                // The symbol server may report a PDB path that does not exist or is not accessible.
                repository.MarkNegativelyCached(key);
                return(null);
            }

            string expectedOutputPath = Path.Combine(randomDirectory.FullName, SymCacheRepository.GetRelativePath(key));
            await RunTranscoderAsync(localPdbPath, randomDirectory.FullName, cancellationToken);

            if (!File.Exists(expectedOutputPath))
            {
                // Transcoding failed for some reason.
                repository.MarkNegativelyCached(key);
                return(null);
            }

            string finalOutputPath = repository.GetPath(key);
            Directory.CreateDirectory(Path.GetDirectoryName(finalOutputPath));

            try
            {
                File.Move(expectedOutputPath, finalOutputPath);
            }
            catch
            {
                // In case of concurrency, the file may already exist. If so, the transcode is done.
                if (!File.Exists(finalOutputPath))
                {
                    // But if not, the transcode failed.
                    repository.MarkNegativelyCached(key);
                    return(null);
                }
            }

            return(finalOutputPath);
        }
    }