示例#1
0
        protected override async Task <StorageContent> OnLoadAsync(Uri resourceUri, CancellationToken cancellationToken)
        {
            // the Azure SDK will treat a starting / as an absolute URL,
            // while we may be working in a subdirectory of a storage container
            // trim the starting slash to treat it as a relative path
            string name = GetName(resourceUri).TrimStart('/');

            CloudBlockBlob blob = GetBlockBlobReference(name);

            await _throttle.WaitAsync();

            try
            {
                string content;

                using (var originalStream = new MemoryStream())
                {
                    await blob.DownloadToStreamAsync(originalStream, cancellationToken);

                    originalStream.Seek(0, SeekOrigin.Begin);

                    if (blob.Properties.ContentEncoding == "gzip")
                    {
                        using (var uncompressedStream = new GZipStream(originalStream, CompressionMode.Decompress))
                        {
                            using (var reader = new StreamReader(uncompressedStream))
                            {
                                content = await reader.ReadToEndAsync();
                            }
                        }
                    }
                    else
                    {
                        using (var reader = new StreamReader(originalStream))
                        {
                            content = await reader.ReadToEndAsync();
                        }
                    }
                }

                return(new StringStorageContentWithETag(content, blob.Properties.ETag));
            }
            catch (StorageException ex) when(ex.RequestInformation?.HttpStatusCode == (int)HttpStatusCode.NotFound)
            {
                if (Verbose)
                {
                    Trace.WriteLine(string.Format("Can't load '{0}'. Blob doesn't exist", resourceUri));
                }

                return(null);
            }
            finally
            {
                _throttle.Release();
            }
        }
示例#2
0
        private async Task <T> ReadAsync <T>(
            HiveType hive,
            string path,
            string typeName,
            bool allow404)
        {
            var blob = GetBlobReference(hive, path);

            _logger.LogInformation(
                "Reading {TypeName} from container {Container} at path {Path}.",
                typeName,
                GetContainerName(hive),
                path);

            await _throttle.WaitAsync();

            try
            {
                T result;
                using (var blobStream = await blob.OpenReadAsync(AccessCondition.GenerateEmptyCondition()))
                {
                    Stream readStream;
                    if (blob.Properties.ContentEncoding == "gzip")
                    {
                        readStream = new GZipStream(blobStream, CompressionMode.Decompress);
                    }
                    else
                    {
                        readStream = blobStream;
                    }

                    using (readStream)
                        using (var streamReader = new StreamReader(readStream))
                            using (var jsonTextReader = new JsonTextReader(streamReader))
                            {
                                result = NuGetJsonSerialization.Serializer.Deserialize <T>(jsonTextReader);
                            }
                }

                _logger.LogInformation(
                    "Finished reading {TypeName} from container {Container} at path {Path} with Content-Encoding {ContentEncoding}.",
                    typeName,
                    GetContainerName(hive),
                    path,
                    blob.Properties.ContentEncoding);

                return(result);
            }
            catch (StorageException ex) when(ex.RequestInformation?.HttpStatusCode == (int)HttpStatusCode.NotFound)
            {
                _logger.LogInformation(
                    "No blob in container {Container} at path {Path} exists.",
                    GetContainerName(hive),
                    path,
                    blob.Properties.ContentEncoding);

                if (allow404)
                {
                    return(default(T));
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                _throttle.Release();
            }
        }
示例#3
0
        /// <summary>
        /// Read bytes from the request URL.
        /// </summary>
        /// <param name="srcOffset">The position from the beginning of the request URL's response body to start reading.</param>
        /// <param name="dst">The destination buffer to write bytes to.</param>
        /// <param name="dstOffset">The offset in the destination buffer.</param>
        /// <param name="count">The maximum number of bytes to read.</param>
        /// <returns>The number of bytes read.</returns>
        public async Task <int> ReadAsync(long srcOffset, byte[] dst, int dstOffset, int count)
        {
            return(await RetryHelper.RetryAsync(async lastException =>
            {
                await _httpThrottle.WaitAsync();
                try
                {
                    using (var request = new HttpRequestMessage(HttpMethod.Get, _requestUri))
                    {
                        if (_sendXMsVersionHeader)
                        {
                            request.Headers.TryAddWithoutValidation("x-ms-version", "2013-08-15");
                        }

                        if (_etag != null)
                        {
                            if (lastException is MiniZipHttpException ex &&
                                ex.StatusCode == HttpStatusCode.PreconditionFailed &&
                                _allowETagVariants == true)
                            {
                                // Swap out the etag version (quoted vs. unquoted, related to an old Azure Blob Storage
                                // bug) if there is an HTTP 412 Precondition Failed. This may be caused by the wrong
                                // version of the etag being cached by an intermediate layer.
                                _etag = _etag == _etagNoQuotes ? _etagQuotes : _etagNoQuotes;
                            }

                            request.Headers.TryAddWithoutValidation("If-Match", _etag);
                        }

                        request.Headers.Range = new RangeHeaderValue(srcOffset, (srcOffset + count) - 1);

                        using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
                        {
                            if (response.StatusCode != HttpStatusCode.PartialContent)
                            {
                                throw await response.ToHttpExceptionAsync(string.Format(
                                                                              Strings.NonPartialContentHttpResponse,
                                                                              (int)response.StatusCode,
                                                                              response.ReasonPhrase));
                            }

                            if (_requireContentRange || response.Content.Headers.ContentRange != null)
                            {
                                if (response.Content.Headers.ContentRange == null)
                                {
                                    throw await response.ToHttpExceptionAsync(Strings.ContentRangeHeaderNotFound);
                                }

                                if (!response.Content.Headers.ContentRange.HasRange ||
                                    response.Content.Headers.ContentRange.Unit != HttpConstants.BytesUnit ||
                                    response.Content.Headers.ContentRange.From != srcOffset ||
                                    response.Content.Headers.ContentRange.To != (srcOffset + count) - 1)
                                {
                                    throw await response.ToHttpExceptionAsync(Strings.InvalidContentRangeHeader);
                                }

                                if (response.Content.Headers.ContentRange.Length != _length)
                                {
                                    throw await response.ToHttpExceptionAsync(string.Format(
                                                                                  Strings.LengthOfHttpContentChanged,
                                                                                  response.Content.Headers.ContentRange.Length,
                                                                                  _length));
                                }
                            }

                            using (var stream = await response.Content.ReadAsStreamAsync())
                            {
                                return await stream.ReadToEndAsync(dst, dstOffset, count);
                            }
                        }
                    }
                }
                finally
                {
                    _httpThrottle.Release();
                }
            }));
        }
示例#4
0
        private async Task <Tuple <Stream, ILookup <string, string> > > GetStreamAndHeadersAsync(Uri requestUri)
        {
            // Determine if the exists endpoint's length and whether it supports range requests.
            var info = await RetryHelper.RetryAsync(async lastException =>
            {
                using (var request = new HttpRequestMessage(HttpMethod.Head, requestUri))
                {
                    if (SendXMsVersionHeader)
                    {
                        request.Headers.TryAddWithoutValidation("x-ms-version", "2013-08-15");
                    }

                    await _httpThrottle.WaitAsync();
                    try
                    {
                        using (var response = await _httpClient.SendAsync(request))
                        {
                            if (!response.IsSuccessStatusCode)
                            {
                                throw await response.ToHttpExceptionAsync(
                                    string.Format(
                                        Strings.UnsuccessfulHttpStatusCodeWhenGettingLength,
                                        (int)response.StatusCode,
                                        response.ReasonPhrase));
                            }

                            if (response.Content?.Headers?.ContentLength == null)
                            {
                                throw await response.ToHttpExceptionAsync(Strings.ContentLengthHeaderNotFound);
                            }

                            // If unspecified, only allow etag variants if the server appears to be Azure Blob Storage.
                            bool allowETagVariants;
                            if (!AllowETagVariants.HasValue &&
                                response.Headers.TryGetValues("x-ms-version", out _) &&
                                response.Headers.TryGetValues("x-ms-blob-type", out _))
                            {
                                allowETagVariants = true;
                            }
                            else
                            {
                                allowETagVariants = AllowETagVariants.GetValueOrDefault(false);
                            }

                            if (RequireAcceptRanges &&
                                (response.Headers.AcceptRanges == null ||
                                 !response.Headers.AcceptRanges.Contains(HttpConstants.BytesUnit)))
                            {
                                throw await response.ToHttpExceptionAsync(string.Format(
                                                                              Strings.AcceptRangesBytesValueNotFoundFormat,
                                                                              HttpConstants.BytesUnit));
                            }

                            var length = response.Content.Headers.ContentLength.Value;

                            var etagBehavior = ETagBehavior;

                            // Even though we may be sending the x-ms-version header to Azure Blob Storage, it's
                            // possible a CDN or other intermediate layer has cached the response ETag header without
                            // quotes. This is why we don't use the parsed response.Headers.ETag value.
                            string etag = null;
                            if (response.Headers.TryGetValues("ETag", out var etags) && etags.Any())
                            {
                                etag = etags.First();
                            }

                            if (etag != null && (etag.StartsWith("W/") || etagBehavior == ETagBehavior.Ignore))
                            {
                                etag = null;
                            }

                            if (etag == null && etagBehavior == ETagBehavior.Required)
                            {
                                throw await response.ToHttpExceptionAsync(string.Format(
                                                                              Strings.MissingETagHeader,
                                                                              nameof(MiniZip.ETagBehavior),
                                                                              nameof(ETagBehavior.Required)));
                            }

                            var headers = Enumerable.Empty <KeyValuePair <string, IEnumerable <string> > >()
                                          .Concat(response.Headers)
                                          .Concat(response.Content.Headers)
                                          .SelectMany(x => x.Value.Select(y => new { x.Key, Value = y }))
                                          .ToLookup(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase);

                            return(new { Length = length, ETag = etag, AllowETagVariants = allowETagVariants, Headers = headers });
                        }
                    }
                    finally
                    {
                        _httpThrottle.Release();
                    }
                }
            });

            var httpRangeReader = new HttpRangeReader(
                _httpClient,
                requestUri,
                info.Length,
                info.ETag,
                RequireContentRange,
                SendXMsVersionHeader,
                info.AllowETagVariants,
                _httpThrottle);

            var bufferSizeProvider = BufferSizeProvider ?? NullBufferSizeProvider.Instance;
            var stream             = new BufferedRangeStream(httpRangeReader, info.Length, bufferSizeProvider);

            return(Tuple.Create <Stream, ILookup <string, string> >(stream, info.Headers));
        }