Example #1
0
        private static async Task SmallAsync()
        {
            var url = "https://api.nuget.org/v3-flatcontainer/newtonsoft.json/10.0.3/newtonsoft.json.10.0.3.nupkg";

            using (var httpClient = new HttpClient())
            {
                var httpZipProvider = new HttpZipProvider(httpClient);

                using (var zipDirectoryReader = await httpZipProvider.GetReaderAsync(new Uri(url)))
                {
                    var zipDirectory = await zipDirectoryReader.ReadAsync();

                    Console.WriteLine("Top 5 ZIP entries by compressed size:");
                    var entries = zipDirectory
                                  .Entries
                                  .OrderByDescending(x => x.GetCompressedSize())
                                  .Take(5)
                                  .ToList();
                    for (var i = 0; i < entries.Count; i++)
                    {
                        Console.WriteLine($"{i + 1}. {entries[i].GetName()}");
                    }
                }
            }
        }
Example #2
0
 public GetReaderAsync()
 {
     _getResponse = request => new HttpResponseMessage(HttpStatusCode.NotFound);
     _httpClient  = new HttpClient(new TestMessageHandler(r => _getResponse(r)));
     _target      = new HttpZipProvider(_httpClient);
     _requestUri  = new Uri("http://example/foo.zip");
 }
Example #3
0
            public async Task UsesETagWhenRequiringAnETag()
            {
                // Arrange
                var fileName = "System.IO.Compression/refzipfiles/normal.zip";

                using (var server = TestUtility.GetTestServer(TestUtility.TestDataDirectory))
                    using (var client = server.CreateClient())
                    {
                        var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), fileName);
                        var target     = new HttpZipProvider(client)
                        {
                            ETagBehavior = ETagBehavior.Required
                        };

                        // Act
                        var reader = await target.GetReaderAsync(requestUri);

                        // Assert
                        var actual = await reader.ReadAsync();

                        var expected = await TestUtility.ReadWithMiniZipAsync(TestUtility.BufferTestData(fileName));

                        TestUtility.VerifyJsonEquals(expected.Data, actual);
                    }
            }
Example #4
0
        public async Task WithSelfUsingHttpRangeReader(string path)
        {
            // Arrange
            using (var memoryStream = TestUtility.BufferTestData(path))
                using (var server = TestUtility.GetTestServer(TestUtility.TestDataDirectory))
                    using (var client = server.CreateClient())
                    {
                        var httpZipProvider = new HttpZipProvider(client)
                        {
                            ETagBehavior         = ETagBehavior.Required,
                            FirstBufferSize      = 1,
                            SecondBufferSize     = 1,
                            BufferGrowthExponent = 2,
                        };

                        var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), path);
                        using (var bufferedRangeStream = await httpZipProvider.GetStreamAsync(requestUri))
                        {
                            // Act
                            var a = await TestUtility.ReadWithMiniZipAsync(memoryStream);

                            var b = await TestUtility.ReadWithMiniZipAsync(bufferedRangeStream);

                            // Assert
                            TestUtility.VerifyJsonEquals(a.Data, b.Data);
                            Assert.Equal(a.Success, b.Success);
                            Assert.Equal(a.Exception?.Message, b.Exception?.Message);
                            Assert.Equal(a.Exception?.GetType(), b.Exception?.GetType());
                        }
                    }
        }
Example #5
0
        public async Task CanReadSignatureFile(string id, string version)
        {
            using (var testDirectory = TestDirectory.Create())
            {
                var packageUri = await NuGetUtility.GetNupkgUrlAsync(id, version);

                using (var httpClient = new HttpClient())
                {
                    var httpZipProvider = new HttpZipProvider(httpClient)
                    {
                        RequireAcceptRanges = false,
                    };
                    using (var reader = await httpZipProvider.GetReaderAsync(packageUri))
                    {
                        // Read the ZIP directory from the .nupkg URL.
                        var zipDirectory = await reader.ReadAsync();

                        // Find the signature entry
                        var entry = zipDirectory.Entries.Single(x => x.GetName() == ".signature.p7s");

                        // Read the signature file
                        var signatureBytes = await reader.ReadUncompressedFileDataAsync(zipDirectory, entry);

                        // Decode the signature file
                        var cms = new SignedCms();
                        cms.Decode(signatureBytes);
                        Assert.NotEmpty(cms.Certificates);
                    }
                }
            }
        }
            public async Task HandlesChangingETagProperly(ETagBehavior etagBehavior, bool success)
            {
                // Arrange
                using (var directory = TestDirectory.Create())
                {
                    var fileName  = "normal.zip";
                    var serverDir = Path.Combine(directory, TestUtility.TestServerDirectory);
                    var filePath  = Path.Combine(serverDir, fileName);
                    Directory.CreateDirectory(Path.GetDirectoryName(filePath));

                    using (var server = TestUtility.GetTestServer(
                               serverDir,
                               etags: true,
                               middleware: async(context, next) =>
                    {
                        await next.Invoke();

                        File.SetLastWriteTimeUtc(filePath, DateTime.UtcNow);
                    }))
                        using (var client = server.CreateClient())
                        {
                            File.Copy(
                                Path.Combine(TestUtility.TestDataDirectory, "System.IO.Compression/refzipfiles/normal.zip"),
                                filePath);
                            var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), fileName);
                            var target     = new HttpZipProvider(client)
                            {
                                ETagBehavior = etagBehavior
                            };
                            var reader = await target.GetReaderAsync(requestUri);

                            // Act & Assert
                            if (success)
                            {
                                var actual = await reader.ReadAsync();

                                var expected = await TestUtility.ReadWithMiniZipAsync(TestUtility.BufferTestData(filePath));

                                TestUtility.VerifyJsonEquals(expected.Data, actual);
                            }
                            else
                            {
                                var ex = await Assert.ThrowsAsync <MiniZipHttpException>(() => reader.ReadAsync());

                                Assert.StartsWith(
                                    "The HTTP response did not have the expected status code HTTP 206 Partial Content. The response was 412 Precondition Failed.",
                                    ex.Message);
                                Assert.Equal(HttpStatusCode.PreconditionFailed, ex.StatusCode);
                                Assert.Equal("Precondition Failed", ex.ReasonPhrase);
                                Assert.StartsWith("HTTP/", ex.DebugResponse);
                            }
                        }
                }
            }
Example #7
0
        public async Task CanGatherAndRecreateNuGetPackageCentralDirectory()
        {
            using (var testDirectory = TestDirectory.Create())
            {
                // Discover the .nupkg URL.
                var sourceRepository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
                var serviceIndex     = await sourceRepository.GetResourceAsync <ServiceIndexResourceV3>();

                var packageBaseAddress = serviceIndex.GetServiceEntryUri(ServiceTypes.PackageBaseAddress);

                var id         = "Newtonsoft.Json".ToLowerInvariant();
                var version    = NuGetVersion.Parse("9.0.1").ToNormalizedString().ToLowerInvariant();
                var packageUri = new Uri(packageBaseAddress, $"{id}/{version}/{id}.{version}.nupkg");

                ZipDirectory zipDirectoryA;
                string       mzipPath;
                using (var httpClient = new HttpClient())
                {
                    var httpZipProvider = new HttpZipProvider(httpClient);
                    using (var reader = await httpZipProvider.GetReaderAsync(packageUri))
                    {
                        // Read the ZIP directory from the .nupkg URL.
                        zipDirectoryA = await reader.ReadAsync();

                        // Save the .mzip to the test directory.
                        mzipPath = Path.Combine(testDirectory, $"{id}.{version}.mzip");
                        using (var fileStream = new FileStream(mzipPath, FileMode.Create))
                        {
                            var mzipFormat = new MZipFormat();
                            await mzipFormat.WriteAsync(reader.Stream, fileStream);
                        }
                    }
                }

                // Read the .mzip back from disk.
                ZipDirectory zipDirectoryB;
                using (var fileStream = new FileStream(mzipPath, FileMode.Open))
                {
                    var mzipFormat = new MZipFormat();
                    using (var mzipStream = await mzipFormat.ReadAsync(fileStream))
                        using (var reader = new ZipDirectoryReader(mzipStream))
                        {
                            zipDirectoryB = await reader.ReadAsync();
                        }
                }

                // Compare the results.
                TestUtility.VerifyJsonEquals(zipDirectoryA, zipDirectoryB);
            }
        }
            public async Task RejectsChangedLength()
            {
                // Arrange
                using (var directory = TestDirectory.Create())
                {
                    var fileName  = "empty.zip";
                    var serverDir = Path.Combine(directory, TestUtility.TestServerDirectory);
                    var filePath  = Path.Combine(serverDir, fileName);
                    Directory.CreateDirectory(Path.GetDirectoryName(filePath));

                    using (var server = TestUtility.GetTestServer(
                               serverDir,
                               etags: true,
                               middleware: async(context, next) =>
                    {
                        await next.Invoke();

                        File.Delete(filePath);
                        File.Copy(
                            Path.Combine(TestUtility.TestDataDirectory, "System.IO.Compression/refzipfiles/normal.zip"),
                            filePath);
                    }))
                        using (var client = server.CreateClient())
                        {
                            File.Copy(
                                Path.Combine(TestUtility.TestDataDirectory, "System.IO.Compression/refzipfiles/empty.zip"),
                                filePath);
                            var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), fileName);
                            var target     = new HttpZipProvider(client)
                            {
                                ETagBehavior = ETagBehavior.Ignore
                            };
                            var reader = await target.GetReaderAsync(requestUri);

                            // Act & Assert
                            var ex = await Assert.ThrowsAsync <MiniZipHttpException>(() => reader.ReadAsync());

                            Assert.StartsWith(
                                "The length of the ZIP file fetched over HTTP changed from the expected 2671162 bytes to 22 bytes.",
                                ex.Message);
                            Assert.Equal(HttpStatusCode.PartialContent, ex.StatusCode);
                            Assert.Equal("Partial Content", ex.ReasonPhrase);
                            Assert.StartsWith("HTTP/", ex.DebugResponse);
                        }
                }
            }
Example #9
0
        public async Task CanGatherAndRecreateNuGetPackageCentralDirectory(string id, string version)
        {
            using (var testDirectory = TestDirectory.Create())
            {
                var packageUri = await NuGetUtility.GetNupkgUrlAsync(id, version);

                ZipDirectory zipDirectoryA;
                string       mzipPath;
                using (var httpClient = new HttpClient())
                {
                    var httpZipProvider = new HttpZipProvider(httpClient)
                    {
                        RequireAcceptRanges = false,
                    };
                    using (var reader = await httpZipProvider.GetReaderAsync(packageUri))
                    {
                        // Read the ZIP directory from the .nupkg URL.
                        zipDirectoryA = await reader.ReadAsync();

                        // Save the .mzip to the test directory.
                        mzipPath = Path.Combine(testDirectory, $"{id}.{version}.mzip");
                        using (var fileStream = new FileStream(mzipPath, FileMode.Create))
                        {
                            var mzipFormat = new MZipFormat();
                            await mzipFormat.WriteAsync(reader.Stream, fileStream);
                        }
                    }
                }

                // Read the .mzip back from disk.
                ZipDirectory zipDirectoryB;
                using (var fileStream = new FileStream(mzipPath, FileMode.Open))
                {
                    var mzipFormat = new MZipFormat();
                    using (var mzipStream = await mzipFormat.ReadAsync(fileStream))
                        using (var reader = new ZipDirectoryReader(mzipStream))
                        {
                            zipDirectoryB = await reader.ReadAsync();
                        }
                }

                // Compare the results.
                TestUtility.VerifyJsonEquals(zipDirectoryA, zipDirectoryB);
            }
        }
            public async Task ReturnsHeadersAsProperties()
            {
                // Arrange
                var fileName = "System.IO.Compression/refzipfiles/normal.zip";

                using (var server = TestUtility.GetTestServer(TestUtility.TestDataDirectory))
                    using (var client = server.CreateClient())
                    {
                        var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), fileName);
                        var target     = new HttpZipProvider(client);

                        // Act
                        var reader = await target.GetReaderAsync(requestUri);

                        // Assert
                        Assert.Equal("2671162", Assert.Single(reader.Properties["Content-Length"]));
                        Assert.Equal("application/x-zip-compressed", Assert.Single(reader.Properties["Content-Type"]));
                    }
            }
Example #11
0
            public async Task FailsWhenRequiredETagIsMissing()
            {
                // Arrange
                var fileName = "System.IO.Compression/refzipfiles/normal.zip";

                using (var server = TestUtility.GetTestServer(TestUtility.TestDataDirectory, etags: false))
                    using (var client = server.CreateClient())
                    {
                        var requestUri = new Uri(new Uri(server.BaseAddress, TestUtility.TestServerDirectory + "/"), fileName);
                        var target     = new HttpZipProvider(client)
                        {
                            ETagBehavior = ETagBehavior.Required
                        };

                        // Act & Assert
                        var ex = await Assert.ThrowsAsync <MiniZipException>(() => target.GetReaderAsync(requestUri));

                        Assert.Equal("An ETag header is required when using ETagBehavior.Required.", ex.Message);
                    }
            }
Example #12
0
        private static async Task LargeAsync()
        {
            // Use the top 5 NuGet packages as an example.
            var urls = new[]
            {
                "https://api.nuget.org/v3-flatcontainer/newtonsoft.json/10.0.3/newtonsoft.json.10.0.3.nupkg",
                "https://api.nuget.org/v3-flatcontainer/nunit/3.9.0/nunit.3.9.0.nupkg",
                "https://api.nuget.org/v3-flatcontainer/entityframework/6.2.0/entityframework.6.2.0.nupkg",
                "https://api.nuget.org/v3-flatcontainer/jquery/3.2.1/jquery.3.2.1.nupkg",
                "https://api.nuget.org/v3-flatcontainer/htmlagilitypack/1.6.7/htmlagilitypack.1.6.7.nupkg",
            };

            // Set up and HTTP client that logs HTTP requests, to help clarify this example.
            using (var httpClientHandler = new HttpClientHandler())
                using (var loggingHandler = new LoggingHandler {
                    InnerHandler = httpClientHandler
                })
                    using (var httpClient = new HttpClient(loggingHandler))
                    {
                        // This provider uses and HTTP client and initializes a ZipDirectoryReader from a URL. This URL
                        // must support HEAD method, Content-Length response header, and Range request header.
                        var httpZipProvider = new HttpZipProvider(httpClient);

                        foreach (var url in urls)
                        {
                            Console.WriteLine(new string('=', 40));
                            Console.WriteLine();

                            // Initialize the reader. This performs a HEAD request to determine if the length of the
                            // ZIP file and whether the URL supports Range requests.
                            using (var reader = await httpZipProvider.GetReaderAsync(new Uri(url)))
                            {
                                // Read the ZIP file by requesting just the Central Directory part of the .zip.
                                var zipDirectory = await reader.ReadAsync();

                                // At this point, we known all about the entries of the .zip file include name, compressed
                                // size, and relative offset in the .zip file.
                                Console.WriteLine("Top 5 ZIP entries by compressed size:");
                                var entries = zipDirectory
                                              .Entries
                                              .OrderByDescending(x => x.GetCompressedSize())
                                              .Take(5)
                                              .ToList();
                                for (var i = 0; i < entries.Count; i++)
                                {
                                    Console.WriteLine($"{i + 1}. {entries[i].GetName()} ({entries[i].GetCompressedSize():N0} bytes)");
                                }
                            }

                            Console.WriteLine();
                        }

                        Console.WriteLine(new string('=', 40));
                        Console.WriteLine();

                        // Summarize the work done. For NuGet packages, it is very common to download less than 1% of the
                        // package content while determining the .zip entry metadata.
                        var ratio = ((double)loggingHandler.TotalResponseBodyBytes) / loggingHandler.TotalContentLength;
                        Console.WriteLine($"Total ZIP files checked:    {urls.Length:N0}");
                        Console.WriteLine($"Total HTTP requests:        {loggingHandler.TotalRequests:N0}");
                        Console.WriteLine($"Total Content-Length bytes: {loggingHandler.TotalContentLength:N0}");
                        Console.WriteLine($"Actual downloaded bytes:    {loggingHandler.TotalResponseBodyBytes:N0}"); // well, sort of...
                        Console.WriteLine($"Downloaded %:               {Math.Round(ratio * 100, 3):0.000}%");
                    }
        }
        public async Task Run()
        {
            var name1    = "CloudBlockBlob and ZipArchive";
            var average1 = await ExecuteTestsAsync(
                name1,
                async (url) =>
            {
                var blobClient = new CloudBlobClient(
                    new Uri("https://example"),
                    new FixUpBlobStorageHandler
                {
                    InnerHandler = new HttpClientDelegatingHandler(_client),
                });
                blobClient.DefaultRequestOptions.DisableContentMD5Validation = true;
                var blob = new CloudBlockBlob(url, blobClient);

                using (var stream = await blob.OpenReadAsync())
                    using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read))
                    {
                        var entries = zipArchive.Entries.ToList();
                    }
            });

            var name2    = "CloudBlockBlob and ZipDirectoryReader";
            var average2 = await ExecuteTestsAsync(
                name2,
                async (url) =>
            {
                var blobClient = new CloudBlobClient(
                    new Uri("https://example"),
                    new FixUpBlobStorageHandler
                {
                    InnerHandler = new HttpClientDelegatingHandler(_client),
                });
                blobClient.DefaultRequestOptions.DisableContentMD5Validation = true;
                var blob = new CloudBlockBlob(url, blobClient);

                using (var stream = await blob.OpenReadAsync())
                    using (var zipDirectoryReader = new ZipDirectoryReader(stream))
                    {
                        var zipDirectory = await zipDirectoryReader.ReadAsync();
                    }
            });

            var name3    = "HttpZipProvider";
            var average3 = await ExecuteTestsAsync(
                name3,
                async (url) =>
            {
                // Read the entries using HttpClient and MiniZip.
                var httpZipProvider = new HttpZipProvider(_client);
                using (var reader = await httpZipProvider.GetReaderAsync(url))
                {
                    var zipDirectory = await reader.ReadAsync();
                }
            });

            Assert.True(average2 < average1, $"'{name2}' should be less than '{name1}'.");
            Assert.True(average3 < average1, $"'{name3}' should be less than '{name1}'.");

            // This is what we want. Ideally the stream provided by HttpZipProvider performs better than CloudBlockBlob.
            Assert.True(average2 < average3, $"'{name2}' should be less than '{name3}'.");
        }