diff --git a/Directory.Packages.props b/Directory.Packages.props index b63cc5e..d8a7f94 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,5 +21,6 @@ + diff --git a/sample/MultipartUpload/MultipartUpload.csproj b/sample/MultipartUpload/MultipartUpload.csproj new file mode 100644 index 0000000..543d04c --- /dev/null +++ b/sample/MultipartUpload/MultipartUpload.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sample/MultipartUpload/Program.cs b/sample/MultipartUpload/Program.cs new file mode 100644 index 0000000..ad10d9c --- /dev/null +++ b/sample/MultipartUpload/Program.cs @@ -0,0 +1,109 @@ +using CommandLine; +using OSS = AlibabaCloud.OSS.V2; + +namespace Sample.MultipartUpload +{ + public class Program + { + + public class Options + { + [Option("region", Required = true, HelpText = "The region in which the bucket is located.")] + public string? Region { get; set; } + + [Option("endpoint", Required = false, HelpText = "The domain names that other services can use to access OSS.")] + public string? Endpoint { get; set; } + + [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")] + public string? Bucket { get; set; } + + [Option("key", Required = true, HelpText = "The `name` of the object.")] + public string? Key { get; set; } + + [Option("partsize", HelpText = "The part size, 512*1024 as default.")] + public long? PartSize { get; set; } + + [Option("filepath", Required = true, HelpText = "The path of a file to upload.")] + public string? FilePath { get; set; } + } + + public static async Task Main(string[] args) + { + + var parserResult = Parser.Default.ParseArguments(args); + if (parserResult.Errors.Any()) + { + Environment.Exit(1); + } + var option = parserResult.Value; + + // Specify the region and other parameters. + var region = option.Region; + var bucket = option.Bucket; + var endpoint = option.Endpoint; + var key = option.Key; + var filePath = option.FilePath!; + long partSize = option.PartSize ?? 512*1024; + + // Using the SDK's default configuration + // loading credentials values from the environment variables + var cfg = OSS.Configuration.LoadDefault(); + cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider(); + cfg.Region = region; + + if (endpoint != null) + { + cfg.Endpoint = endpoint; + } + + using var client = new OSS.Client(cfg); + + var initResult = await client.InitiateMultipartUploadAsync(new() + { + Bucket = bucket, + Key = key + }); + + // upload + using var file = File.OpenRead(filePath); + long fileSize = file.Length; + long partNumber = 1; + + var uploadParts = new List(); + + for (long offset = 0; offset < fileSize; offset += partSize) + { + var size = Math.Min(partSize, fileSize - offset); + var upResult = await client.UploadPartAsync(new() + { + Bucket = bucket, + Key = key, + PartNumber = partNumber, + UploadId = initResult.UploadId, + Body = new OSS.IO.BoundedStream(file, offset, size) + }); + uploadParts.Add(new () { PartNumber = partNumber, ETag = upResult.ETag }); + partNumber++; + } + + // complete + uploadParts.Sort((left, right) => { return (left.PartNumber > right.PartNumber) ? 1 : -1; }); + var cmResult = await client.CompleteMultipartUploadAsync(new() + { + Bucket = bucket, + Key = key, + UploadId = initResult.UploadId, + CompleteMultipartUpload = new () + { + Parts = uploadParts + } + }); + + Console.WriteLine("MultipartUpload done"); + Console.WriteLine($"StatusCode: {cmResult.StatusCode}"); + Console.WriteLine($"RequestId: {cmResult.RequestId}"); + Console.WriteLine("Response Headers:"); + cmResult.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + x.Value)); + } + } +} diff --git a/sample/PostObject/PostObject.csproj b/sample/PostObject/PostObject.csproj new file mode 100644 index 0000000..e11f9aa --- /dev/null +++ b/sample/PostObject/PostObject.csproj @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/sample/PostObject/Program.cs b/sample/PostObject/Program.cs new file mode 100644 index 0000000..3bf863f --- /dev/null +++ b/sample/PostObject/Program.cs @@ -0,0 +1,145 @@ +using System.Globalization; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using CommandLine; +using OSS = AlibabaCloud.OSS.V2; + +namespace Sample.PostObject +{ + public class Program + { + + public class Options + { + [Option("region", Required = true, HelpText = "The region in which the bucket is located.")] + public string? Region { get; set; } + + [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")] + public string? Bucket { get; set; } + + [Option("key", Required = true, HelpText = "The `name` of the object.")] + public string? Key { get; set; } + } + + static string ToHexString(byte[] data, bool lowercase) + { + var sb = new StringBuilder(); + for (var i = 0; i < data.Length; i++) sb.Append(data[i].ToString(lowercase ? "x2" : "X2")); + return sb.ToString(); + } + + static string quote(string value) + { + return $"\"{value}\""; + } + + public static async Task Main(string[] args) + { + + var parserResult = Parser.Default.ParseArguments(args); + if (parserResult.Errors.Any()) + { + Environment.Exit(1); + } + var option = parserResult.Value; + + // Specify the region and other parameters. + var region = option.Region!; + var bucket = option.Bucket!; + var key = option.Key!; + var product = "oss"; + + // loading credentials values from the environment variables + var credentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider(); + var credentials = credentialsProvider.GetCredentials(); + + var content = "hi oss"; + + // build policy + var utcTime = DateTime.UtcNow; + var date = utcTime.ToUniversalTime().ToString("yyyyMMdd", CultureInfo.InvariantCulture); + var dateTime = utcTime.ToUniversalTime().ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture); + var expiration = utcTime.AddHours(1); + var credentialInfo = $"{credentials.AccessKeyId}/{date}/{region}/{product}/aliyun_v4_request"; + var policyMap = new Dictionary() + { + { "expiration", + expiration.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.000'Z'", CultureInfo.InvariantCulture) + }, + { "conditions", new Object[]{ + new Dictionary() {{ "bucket", bucket } }, + new Dictionary() {{ "x-oss-signature-version", "OSS4-HMAC-SHA256" } }, + new Dictionary() {{ "x-oss-credential", credentialInfo } }, + new Dictionary() {{ "x-oss-date", dateTime } }, + //other condition + new Object[]{"content-length-range", 1, 1024 }, + //new Object[]{"eq", "$success_action_status", "201"}, + //new Object[]{"starts-with", "$key", "user/eric/"}, + //new Object[]{"in", "$content-type", new string[]{"image/jpg", "image/png"}}, + //new Object[]{ "not-in", "$cache-control", new string[]{ "no-cache" } }, + } + }, + }; + + var policy = JsonSerializer.Serialize(policyMap); + + // sign policy + var stringToSign = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy)); + + // signing key + using var kha = new HMACSHA256(); + + var ksecret = Encoding.UTF8.GetBytes("aliyun_v4" + credentials.AccessKeySecret); + + kha.Key = ksecret; + var hashDate = kha.ComputeHash(Encoding.UTF8.GetBytes(date)); + + kha.Key = hashDate; + var hashRegion = kha.ComputeHash(Encoding.UTF8.GetBytes(region)); + + kha.Key = hashRegion; + var hashProduct = kha.ComputeHash(Encoding.UTF8.GetBytes(product)); + + kha.Key = hashProduct; + var signingKey = kha.ComputeHash(Encoding.UTF8.GetBytes("aliyun_v4_request")); + + // Signature + kha.Key = signingKey; + var signature = ToHexString(kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)), true); + + // Multipar form + using var formData = new MultipartFormDataContent(); + // trim quote + var boundary = formData.Headers.ContentType!.Parameters.ElementAt(0).Value!; + formData.Headers.ContentType.Parameters.ElementAt(0).Value = boundary.Trim('"'); + + // object info, key & metadata + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(key)), quote("key")); + // meta-data + //formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(value)), quote("x-oss-")); + // policy + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(stringToSign)), quote("policy")); + // Signature + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes("OSS4-HMAC-SHA256")), quote("x-oss-signature-version")); + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(credentialInfo)), quote("x-oss-credential")); + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(dateTime)), quote("x-oss-date")); + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(signature)), quote("x-oss-signature")); + + // Data + formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(content)), quote("file")); + + // Send request + using var hc = new HttpClient(); + + var result = await hc.PostAsync($"http://{bucket}.oss-{region}.aliyuncs.com/", formData); + + Console.WriteLine("PostObject done"); + Console.WriteLine($"StatusCode: {result.StatusCode}"); + Console.WriteLine("Response Headers:"); + result.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + String.Join(",", x.Value.ToList()))); + } + } +} diff --git a/sample/PresignMultipartUpload/PresignMultipartUpload.csproj b/sample/PresignMultipartUpload/PresignMultipartUpload.csproj new file mode 100644 index 0000000..543d04c --- /dev/null +++ b/sample/PresignMultipartUpload/PresignMultipartUpload.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sample/PresignMultipartUpload/Program.cs b/sample/PresignMultipartUpload/Program.cs new file mode 100644 index 0000000..e63e310 --- /dev/null +++ b/sample/PresignMultipartUpload/Program.cs @@ -0,0 +1,134 @@ +using System.Net.Http; +using CommandLine; +using OSS = AlibabaCloud.OSS.V2; + +namespace Sample.PresignMultipartUpload +{ + public class Program + { + + public class Options + { + [Option("region", Required = true, HelpText = "The region in which the bucket is located.")] + public string? Region { get; set; } + + [Option("endpoint", Required = false, HelpText = "The domain names that other services can use to access OSS.")] + public string? Endpoint { get; set; } + + [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")] + public string? Bucket { get; set; } + + [Option("key", Required = true, HelpText = "The `name` of the object.")] + public string? Key { get; set; } + + [Option("partsize", HelpText = "The part size, 512*1024 as default.")] + public long? PartSize { get; set; } + + [Option("filepath", Required = true, HelpText = "The path of a file to upload.")] + public string? FilePath { get; set; } + } + + public static async Task Main(string[] args) + { + + var parserResult = Parser.Default.ParseArguments(args); + if (parserResult.Errors.Any()) + { + Environment.Exit(1); + } + var option = parserResult.Value; + + // Specify the region and other parameters. + var region = option.Region; + var bucket = option.Bucket; + var endpoint = option.Endpoint; + var key = option.Key; + var filePath = option.FilePath!; + long partSize = option.PartSize ?? 512 * 1024; + + // Using the SDK's default configuration + // loading credentials values from the environment variables + var cfg = OSS.Configuration.LoadDefault(); + cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider(); + cfg.Region = region; + + if (endpoint != null) + { + cfg.Endpoint = endpoint; + } + + using var client = new OSS.Client(cfg); + + var initResult = await client.InitiateMultipartUploadAsync(new() + { + Bucket = bucket, + Key = key + }); + + // presign and upload + using var hc = new HttpClient(); + using var file = File.OpenRead(filePath); + long fileSize = file.Length; + long partNumber = 1; + + for (long offset = 0; offset < fileSize; offset += partSize) + { + var size = Math.Min(partSize, fileSize - offset); + var presignResult = client.Presign(new OSS.Models.UploadPartRequest + { + Bucket = bucket, + Key = key, + PartNumber = partNumber, + UploadId = initResult.UploadId, + Body = new OSS.IO.BoundedStream(file, offset, size) + }); + var httpResult = await hc.PutAsync( + presignResult.Url, + new StreamContent(new OSS.IO.BoundedStream(file, offset, size)) + ); + + if (!httpResult.IsSuccessStatusCode) + { + throw new Exception("upload part fail"); + } + partNumber++; + } + + // list parts + var uploadParts = new List(); + var paginator = client.ListPartsPaginator(new () + { + Bucket = bucket, + Key = key, + UploadId = initResult.UploadId, + }); + + await foreach (var page in paginator.IterPageAsync()) + { + foreach (var part in page.Parts ?? []) + { + uploadParts.Add(new () { PartNumber = part.PartNumber, ETag = part.ETag }); + } + } + + // complete + uploadParts.Sort((left, right) => { return (left.PartNumber > right.PartNumber) ? 1 : -1; }); + var cmResult = await client.CompleteMultipartUploadAsync(new() + { + Bucket = bucket, + Key = key, + UploadId = initResult.UploadId, + CompleteMultipartUpload = new() + { + Parts = uploadParts + } + }); + + Console.WriteLine("MultipartUpload done"); + Console.WriteLine($"StatusCode: {cmResult.StatusCode}"); + Console.WriteLine($"RequestId: {cmResult.RequestId}"); + Console.WriteLine("Response Headers:"); + cmResult.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + x.Value)); + } + } +} diff --git a/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln b/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln index c798d7b..0d8d19e 100644 --- a/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln +++ b/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln @@ -80,6 +80,11 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeleteBucket", "DeleteBucket\DeleteBucket.csproj", "{1CFCF2DD-C9F6-4903-A530-C7B813A6ED27}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AliyunCredentialsUsage", "AliyunCredentialsUsage\AliyunCredentialsUsage.csproj", "{02A88682-C702-461A-BE45-03FE071774F1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultipartUpload", "MultipartUpload\MultipartUpload.csproj", "{CEC023D4-F319-47E0-B6AA-A3B028A81FCB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresignMultipartUpload", "PresignMultipartUpload\PresignMultipartUpload.csproj", "{84BC7ED8-AF75-462D-8969-1ADCAD75387B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostObject", "PostObject\PostObject.csproj", "{7816C6AA-0730-4CE7-B9AA-469BC85A136B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -243,6 +248,18 @@ Global {02A88682-C702-461A-BE45-03FE071774F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {02A88682-C702-461A-BE45-03FE071774F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {02A88682-C702-461A-BE45-03FE071774F1}.Release|Any CPU.Build.0 = Release|Any CPU + {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Release|Any CPU.Build.0 = Release|Any CPU + {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Release|Any CPU.Build.0 = Release|Any CPU + {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs index 66c6387..88f1e0e 100644 --- a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs +++ b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs @@ -1,10 +1,13 @@  +using System; + namespace AlibabaCloud.OSS.V2.Credentials { /// - /// CredentialsProviderFunc provides a helper wrapping a function value + /// CredentialsProvideFunc provides a helper wrapping a function value /// to satisfy the ICredentialsProvider interface. /// + [Obsolete("Please use CredentialsProviderFunc instead")] public class CredentialsProvideFunc : ICredentialsProvider { public delegate Credentials GetCredentialsFunc(); diff --git a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs new file mode 100644 index 0000000..de923cb --- /dev/null +++ b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs @@ -0,0 +1,25 @@ + +namespace AlibabaCloud.OSS.V2.Credentials +{ + /// + /// CredentialsProviderFunc provides a helper wrapping a function value + /// to satisfy the ICredentialsProvider interface. + /// + public class CredentialsProviderFunc : ICredentialsProvider + { + public delegate Credentials GetCredentialsFunc(); + + private readonly GetCredentialsFunc _func; + /// + /// Creates an instance of + /// + /// a function that returns Credentials + public CredentialsProviderFunc(GetCredentialsFunc func) => _func = func; + + /// + public Credentials GetCredentials() + { + return _func(); + } + } +} diff --git a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs index 1bfdace..8a6c7d0 100644 --- a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs +++ b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs @@ -1,9 +1,12 @@  +using System; + namespace AlibabaCloud.OSS.V2.Credentials { /// /// Explicitly specify the AccessKey pair that you want to use to access OSS. /// + [Obsolete("Please use StaticCredentialsProvider instead")] public class StaticCredentialsProvide : ICredentialsProvider { private readonly Credentials _credentials; diff --git a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs new file mode 100644 index 0000000..844e78b --- /dev/null +++ b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs @@ -0,0 +1,37 @@ + +namespace AlibabaCloud.OSS.V2.Credentials +{ + /// + /// Explicitly specify the AccessKey pair that you want to use to access OSS. + /// + public class StaticCredentialsProvider : ICredentialsProvider + { + private readonly Credentials _credentials; + /// + /// Creates an instance of + /// + /// + /// + public StaticCredentialsProvider(string accessKeyId, string accessKeySecret) + { + _credentials = new(accessKeyId, accessKeySecret); + } + + /// + /// Creates an instance of + /// + /// + /// + /// + public StaticCredentialsProvider(string accessKeyId, string accessKeySecret, string securityToken) + { + _credentials = new(accessKeyId, accessKeySecret, securityToken); + } + + /// + public Credentials GetCredentials() + { + return _credentials; + } + } +} diff --git a/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs b/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs new file mode 100644 index 0000000..fe71261 --- /dev/null +++ b/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs @@ -0,0 +1,200 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace AlibabaCloud.OSS.V2.IO +{ + /// + /// Represents read-only view over the portion of underlying stream. + /// + public sealed class BoundedStream : Stream + { + private long _length; + private long _offset; + + /// + /// Creates a new BoundedStream that wraps the given stream. + /// + /// The wrapped input stream. + public BoundedStream(Stream stream) + { + this._length = stream.Length; + BaseStream = stream; + } + + /// + /// Creates a new BoundedStream that wraps the given stream and limits it to a certain size. + /// + /// The wrapped input stream. + /// The offset in the underlying stream. + /// Total number of bytes to allow to be read from the stream. + public BoundedStream(Stream stream, long offset, long length) + { + BaseStream = stream; + Adjust(offset, length); + } + + /// + /// Gets underlying stream. + /// + public Stream BaseStream { get; private set; } + + /// + /// Adjust the stream bounds. + /// + /// + /// This method modifies property of the underlying stream. + /// + /// The offset in the underlying stream. + /// Total number of bytes to allow to be read from the stream. + /// is larger than the remaining length of the underlying stream; or if greater than the length of the underlying stream. + public void Adjust(long offset, long length) + { + ThrowIfGreaterThan((ulong)offset, (ulong)BaseStream.Length, nameof(offset)); + ThrowIfGreaterThan((ulong)length, (ulong)(BaseStream.Length - offset), nameof(length)); + this._length = length; + this._offset = offset; + BaseStream.Position = offset; + } + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// if the stream supports reading; otherwise, . + public override bool CanRead => BaseStream.CanRead; + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + /// if the stream supports seeking; otherwise, . + public override bool CanSeek => BaseStream.CanSeek; + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + /// Always . + public override bool CanWrite => false; + + /// + public override long Length => _length; + + /// + public override long Position + { + get => BaseStream.Position - _offset; + set + { + ThrowIfGreaterThan(value, _length, nameof(value)); + BaseStream.Position = _offset + value; + } + } + + private long RemainingBytes => _length - Position; + + /// + public override void Flush() => BaseStream.Flush(); + + /// + public override Task FlushAsync(CancellationToken token = default) => BaseStream.FlushAsync(token); + + /// + public override bool CanTimeout => BaseStream.CanTimeout; + + /// + public override int ReadByte() + => Position < _length ? BaseStream.ReadByte() : -1; + + /// + public override void WriteByte(byte value) => throw new NotSupportedException(); + + /// + public override int Read(byte[] buffer, int offset, int count) + { + return BaseStream.Read(buffer, offset, (int)Math.Min(count, RemainingBytes)); + } + + /// + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + count = (int)Math.Min(count, RemainingBytes); + return BaseStream.BeginRead(buffer, offset, count, callback, state); + } + + /// + public override int EndRead(IAsyncResult asyncResult) => BaseStream.EndRead(asyncResult); + + /// + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token = default) + => BaseStream.ReadAsync(buffer, offset, (int)Math.Min(count, RemainingBytes), token); + + /// + public override long Seek(long offset, SeekOrigin origin) + { + var newPosition = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.Current => Position + offset, + SeekOrigin.End => _length + offset, + _ => throw new ArgumentOutOfRangeException(nameof(origin)) + }; + + if (newPosition < 0L) + throw new IOException(); + + ThrowIfGreaterThan(newPosition, _length, nameof(offset)); + + Position = newPosition; + return newPosition; + } + + /// + public override void SetLength(long value) + { + ThrowIfGreaterThan((ulong)value, (ulong)(BaseStream.Length - BaseStream.Position), nameof(value)); + _length = value; + } + + /// + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token = default) => Task.FromException(new NotSupportedException()); + + /// + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => throw new NotSupportedException(); + + /// + public override void EndWrite(IAsyncResult asyncResult) => throw new InvalidOperationException(); + + /// + public override int ReadTimeout + { + get => BaseStream.ReadTimeout; + set => BaseStream.ReadTimeout = value; + } + + /// + public override int WriteTimeout + { + get => BaseStream.WriteTimeout; + set => BaseStream.WriteTimeout = value; + } + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + BaseStream.Dispose(); + } + + private static void ThrowIfGreaterThan(T value, T other, string? paramName = null) where T : IComparable + { + if (value.CompareTo(other) > 0) + { + throw new ArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}')must be less than or equal to '{other}'"); + } + } + } +} diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs index da4a7fa..6d13a0d 100644 --- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs +++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs @@ -1,4 +1,5 @@ using System.Text; +using AlibabaCloud.OSS.V2.IO; using AlibabaCloud.OSS.V2.Models; namespace AlibabaCloud.OSS.V2.IntegrationTests; @@ -516,7 +517,7 @@ public async Task TestPutGetObjectByFilePath() public async Task TestInsecureSkipVerifyTrue() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.InsecureSkipVerify = true; @@ -900,7 +901,7 @@ public async Task TestPutGetObjectByFilePathWithProgress() // disable crc var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; cfg.DisableDownloadCrc64Check = true; @@ -1005,4 +1006,106 @@ public async Task TestQueryWithSpecialChar() var got1 = await reader1.ReadToEndAsync(); Assert.Equal(content, got1); } + + [Fact] + public async Task TestUploadPartFromFile() + { + var client = Utils.GetDefaultClient(); + + //default + var bucketName = Utils.RandomBucketName(BucketNamePrefix); + + var result = await client.PutBucketAsync(new() + { + Bucket = bucketName + }); + + Assert.NotNull(result); + Assert.Equal(200, result.StatusCode); + Assert.NotNull(result.RequestId); + + var partSize = 120 * 1024; + var filepath = Utils.RandomFilePath(RootPath); + var saveFilepath = Utils.RandomFilePath(RootPath); + Utils.PrepareSampleFile(filepath, 512); + Assert.True(File.Exists(filepath)); + + // init + var objectName = Utils.RandomObjectName(); + var initResult = await client.InitiateMultipartUploadAsync(new() + { + Bucket = bucketName, + Key = objectName + } + ); + Assert.NotNull(initResult); + Assert.Equal(200, initResult.StatusCode); + Assert.NotNull(initResult.RequestId); + Assert.Equal("url", initResult.EncodingType); + + // upload + using var file = File.OpenRead(filepath); + var fileSize = file.Length; + long partNumber = 1; + for (int offset = 0; offset < fileSize; offset += partSize) + { + var size = (long)Math.Min(partSize, fileSize - offset); + var upResult = await client.UploadPartAsync(new() + { + Bucket = bucketName, + Key = objectName, + PartNumber = partNumber, + UploadId = initResult.UploadId, + Body = new BoundedStream(file, (long)offset, size) + }); + + partNumber++; + + Assert.NotNull(upResult); + Assert.Equal(200, upResult.StatusCode); + Assert.NotNull(upResult.RequestId); + } + + // complete + var cmResult = await client.CompleteMultipartUploadAsync(new() + { + Bucket = bucketName, + Key = objectName, + UploadId = initResult.UploadId, + CompleteAll = "yes" + }); + Assert.NotNull(cmResult); + Assert.Equal(200, cmResult.StatusCode); + Assert.NotNull(cmResult.RequestId); + Assert.Equal("url", cmResult.EncodingType); + + // get object + var getObjectResult = await client.GetObjectAsync( + new() + { + Bucket = bucketName, + Key = objectName + } + ); + Assert.NotNull(getObjectResult); + Assert.Equal(200, getObjectResult.StatusCode); + Assert.Equal((long)fileSize, getObjectResult.ContentLength); + Assert.Equal("Multipart", getObjectResult.ObjectType); + Assert.Null(getObjectResult.TaggingCount); + Assert.Null(getObjectResult.ContentMd5); + Assert.NotNull(getObjectResult.StorageClass); + Assert.NotNull(getObjectResult.Metadata); + Assert.Empty(getObjectResult.Metadata); + Assert.NotNull(getObjectResult.Body); + Assert.False(getObjectResult.Body.CanSeek); + + using var saveStream = File.OpenWrite(saveFilepath); + await getObjectResult.Body.CopyToAsync(saveStream); + saveStream.Close(); + + var srcMd5 = Utils.ComputeContentMd5(filepath); + var dstMd5 = Utils.ComputeContentMd5(saveFilepath); + Assert.NotEmpty(srcMd5); + Assert.Equal(srcMd5, dstMd5); + } } diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs index 4c295ff..0847c08 100644 --- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs +++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs @@ -781,7 +781,7 @@ await invClient.ListMultipartUploadsAsync( public async Task TestUploadPartWithCrcCheckDisable() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; cfg.DisableUploadCrc64Check = true; diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs index f3df47a..751bbd8 100644 --- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs +++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs @@ -1526,7 +1526,7 @@ public async Task TestAppendObjectWithCrcCheckEnable() public async Task TestAppendObjectWithCrcCheckDisable() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; cfg.DisableUploadCrc64Check = true; @@ -1572,7 +1572,7 @@ public async Task TestAppendObjectWithCrcCheckDisable() public async Task TestPutObjectWithCrcCheckDisable() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; cfg.DisableUploadCrc64Check = true; @@ -1836,7 +1836,7 @@ public async Task TestGetObjectWithCrcCheckEnable() public async Task TestGetObjectWithCrcCheckDisable() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; cfg.DisableDownloadCrc64Check = true; diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs index 3ed547f..3fa102a 100644 --- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs +++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs @@ -196,7 +196,7 @@ out var expires public async Task TestPresignGetAndPutObjectV1() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret); cfg.Region = "cn-shenzhen"; cfg.SignatureVersion = "v1"; @@ -423,7 +423,7 @@ public async Task TestPresignAbortMultipartUpload() public void TestPresignFail() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("", ""); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("", ""); cfg.Region = Utils.Region; cfg.Endpoint = Utils.Endpoint; @@ -546,7 +546,7 @@ public void TestPresignFail() public void TestPresignWithExpirationTimeV4() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk"); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk"); cfg.Region = "cn-shenzhen"; using var client = new Client(cfg); @@ -671,7 +671,7 @@ public void TestPresignWithExpirationTimeV4() public void TestPresignWithExpirationTimeV1() { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk"); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk"); cfg.Region = "cn-shenzhen"; cfg.SignatureVersion = "v1"; @@ -800,7 +800,7 @@ public void TestPresignWithPresignExpirationException() // v1 supports > 7 days Expiration var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk"); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk"); cfg.Region = "cn-shenzhen"; cfg.SignatureVersion = "v1"; @@ -831,7 +831,7 @@ public void TestPresignWithPresignExpirationException() // v4 does not support > 7 days Expiration cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk"); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk"); cfg.Region = "cn-shenzhen"; cfg.SignatureVersion = "v4"; diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs index 590b27f..3cc70f8 100644 --- a/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs +++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs @@ -54,7 +54,7 @@ public static Client GetDefaultClient() if (_client != null) return _client; var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(AccessKeyId, AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(AccessKeyId, AccessKeySecret); cfg.Region = Region; cfg.Endpoint = Endpoint; @@ -66,7 +66,7 @@ public static Client GetDefaultClient() public static Client GetClient(string region, string endpoint) { var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(AccessKeyId, AccessKeySecret); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(AccessKeyId, AccessKeySecret); cfg.Region = region; cfg.Endpoint = endpoint; @@ -78,7 +78,7 @@ public static Client GetInvalidAkClient() if (_invalidClient != null) return _invalidClient; var cfg = Configuration.LoadDefault(); - cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("invalid-ak", "invalid-sk"); + cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("invalid-ak", "invalid-sk"); cfg.Region = Region; cfg.Endpoint = Endpoint; diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs index 250d3e3..dd111e0 100644 --- a/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs +++ b/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs @@ -58,7 +58,7 @@ public void TestCredentials() [Fact] public void TestStaticCredentialsProvide() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); Assert.Equal("ak", cred.AccessKeyId); Assert.Equal("sk", cred.AccessKeySecret); @@ -67,7 +67,7 @@ public void TestStaticCredentialsProvide() Assert.Null(cred.Expiration); Assert.False(cred.IsExpired); - provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token"); + provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token"); cred = provider.GetCredentials(); Assert.Equal("ak", cred.AccessKeyId); Assert.Equal("sk", cred.AccessKeySecret); @@ -94,7 +94,7 @@ public void TestAnonymousCredentialsProvider() public void TestCredentialsProvideFunc() { - var provider = new V2.Credentials.CredentialsProvideFunc(() => new V2.Credentials.Credentials("ak", "sk")); + var provider = new V2.Credentials.CredentialsProviderFunc(() => new V2.Credentials.Credentials("ak", "sk")); var cred = provider.GetCredentials(); Assert.Equal("ak", cred.AccessKeyId); Assert.Equal("sk", cred.AccessKeySecret); @@ -103,7 +103,7 @@ public void TestCredentialsProvideFunc() Assert.Null(cred.Expiration); Assert.False(cred.IsExpired); - provider = new V2.Credentials.CredentialsProvideFunc(() => new V2.Credentials.Credentials("ak", "sk", "token")); + provider = new V2.Credentials.CredentialsProviderFunc(() => new V2.Credentials.Credentials("ak", "sk", "token")); cred = provider.GetCredentials(); Assert.Equal("ak", cred.AccessKeyId); Assert.Equal("sk", cred.AccessKeySecret); diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs new file mode 100644 index 0000000..ee2fc98 --- /dev/null +++ b/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs @@ -0,0 +1,200 @@ +using System.Text; +using AlibabaCloud.OSS.V2.IO; + +namespace AlibabaCloud.OSS.V2.UnitTests.Models; + +public class BoundedStreamTest +{ + [Theory] + [InlineData(0, 4, "This")] + [InlineData(5, 2, "is")] + [InlineData(10, 4, "test")] + public static void AdjustSetsBoundedOfStream(int offset, int length, string expected) + { + using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test")); + using var bounded = new BoundedStream(ms); + bounded.Adjust(offset, length); + using StreamReader reader = new(bounded); + Assert.Equal(expected, reader.ReadToEnd()); + } + + [Theory] + [InlineData(0, 4, "This")] + [InlineData(5, 2, "is")] + [InlineData(10, 4, "test")] + public static void CreatesBoundedOfStream(int offset, int length, string expected) + { + using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test")); + using var bounded = new BoundedStream(ms, offset, length); + using StreamReader reader = new(bounded); + Assert.Equal(expected, reader.ReadToEnd()); + } + + [Fact] + public static void ReadByteSequentially() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + Assert.Same(ms, bounded.BaseStream); + Assert.Equal(0, bounded.Position); + bounded.Adjust(0, 2); + Assert.Equal(1, bounded.ReadByte()); + Assert.Equal(1, bounded.Position); + + Assert.Equal(3, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + + Assert.Equal(-1, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + } + + [Fact] + public static void CreatesAndReadByteSequentially() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms, 0, 2); + Assert.Same(ms, bounded.BaseStream); + Assert.Equal(0, bounded.Position); + + Assert.Equal(1, bounded.ReadByte()); + Assert.Equal(1, bounded.Position); + + Assert.Equal(3, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + + Assert.Equal(-1, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + } + + [Fact] + public static void SetPosition() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + bounded.Adjust(1, 3); + bounded.Position = 1; + Assert.Equal(5, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + bounded.Position = 0; + Assert.Equal(3, bounded.ReadByte()); + Assert.Equal(1, bounded.Position); + } + + [Fact] + public static void CreatesAndSetPosition() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms, 1, 3); + bounded.Position = 1; + Assert.Equal(5, bounded.ReadByte()); + Assert.Equal(2, bounded.Position); + bounded.Position = 0; + Assert.Equal(3, bounded.ReadByte()); + Assert.Equal(1, bounded.Position); + } + + [Fact] + public static void ReadRange() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + bounded.Adjust(1L, 2L); + var buffer = new byte[4]; + Assert.Equal(2, bounded.Read(buffer, 0, buffer.Length)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + Assert.Equal(0, buffer[3]); + //read from the end of the stream + Assert.Equal(-1, bounded.ReadByte()); + } + + [Fact] + public static void CreatesAndReadRange() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms, 1L, 2L); + var buffer = new byte[4]; + Assert.Equal(2, bounded.Read(buffer, 0, buffer.Length)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + Assert.Equal(0, buffer[3]); + //read from the end of the stream + Assert.Equal(-1, bounded.ReadByte()); + } + + [Fact] + public static async Task ReadRangeAsync() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + bounded.Adjust(1L, 2L); + var buffer = new byte[4]; + Assert.Equal(2, await bounded.ReadAsync(buffer, 0, buffer.Length)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + Assert.Equal(0, buffer[3]); + //read from the end of the stream + Assert.Equal(-1, bounded.ReadByte()); + } + + [Fact] + public static async Task CreatesAndReadRangeAsync() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms, 1L, 2L); + var buffer = new byte[4]; + Assert.Equal(2, await bounded.ReadAsync(buffer, 0, buffer.Length)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + Assert.Equal(0, buffer[3]); + //read from the end of the stream + Assert.Equal(-1, bounded.ReadByte()); + } + + [Fact] + public static void ReadApm() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + bounded.Adjust(1L, 2L); + var buffer = new byte[4]; + var ar = bounded.BeginRead(buffer, 0, 2, null, null); + Assert.Equal(2, bounded.EndRead(ar)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + } + + [Fact] + public static void CreatesAndReadApm() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms, 1L, 2L); + var buffer = new byte[4]; + var ar = bounded.BeginRead(buffer, 0, 2, null, null); + Assert.Equal(2, bounded.EndRead(ar)); + Assert.Equal(3, buffer[0]); + Assert.Equal(5, buffer[1]); + Assert.Equal(0, buffer[2]); + } + + [Fact] + public static async Task ExceptionCheck() + { + using var ms = new MemoryStream([1, 3, 5, 8, 12]); + using var bounded = new BoundedStream(ms); + Assert.True(bounded.CanRead); + Assert.True(bounded.CanSeek); + Assert.False(bounded.CanWrite); + Assert.Equal(ms.CanTimeout, bounded.CanTimeout); + Assert.Throws(() => bounded.WriteByte(2)); + Assert.Throws(() => bounded.Write(new byte[3], 0, 3)); + Assert.Throws(() => bounded.BeginWrite(new byte[2], 0, 2, null, null)); + Assert.Throws(() => bounded.EndWrite(Task.CompletedTask)); + await Assert.ThrowsAsync(() => bounded.WriteAsync(new byte[3], 0, 3)); + } +} diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs index 46dda3b..5c5963d 100644 --- a/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs +++ b/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs @@ -854,7 +854,7 @@ public async Task TestSignerV4() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; var client = new ClientImpl(config); @@ -892,7 +892,7 @@ public async Task TestSignerV1() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), SignatureVersion = "v1" }; @@ -1725,7 +1725,7 @@ public void TestPresignInnerV4() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; var client = new ClientImpl(config); @@ -1795,7 +1795,7 @@ public void TestPresignInnerV4() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), AdditionalHeaders = ["Abc"] }; @@ -1842,7 +1842,7 @@ public void TestPresignInnerV4() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk", "token"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk", "token"), HttpTransport = new HttpTransport(mockHandler), }; client = new ClientImpl(config); @@ -1882,7 +1882,7 @@ public void TestPresignInnerV4DefaultExpiration() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; var client = new ClientImpl(config); @@ -1919,7 +1919,7 @@ public void TestPresignInnerV1() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), SignatureVersion = "v1" }; @@ -1985,7 +1985,7 @@ public void TestPresignInnerV1() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), SignatureVersion = "v1", AdditionalHeaders = ["Abc"] @@ -2029,7 +2029,7 @@ public void TestPresignInnerV1() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk", "token"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk", "token"), HttpTransport = new HttpTransport(mockHandler), SignatureVersion = "v1" }; @@ -2068,7 +2068,7 @@ public void TestPresignInnerV1DefaultExpiration() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), SignatureVersion = "v1" }; @@ -2106,7 +2106,7 @@ public void TestPresignMisc() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; var client = new ClientImpl(config); @@ -2164,7 +2164,7 @@ public void TestPresignMisc() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("", ""), + CredentialsProvider = new StaticCredentialsProvider("", ""), HttpTransport = new HttpTransport(mockHandler), }; client = new ClientImpl(config); @@ -2191,7 +2191,7 @@ public void TestPresignMisc() config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; client = new ClientImpl(config); @@ -2226,7 +2226,7 @@ public void TestDispose() var config = new Configuration() { Region = "cn-hangzhou", - CredentialsProvider = new StaticCredentialsProvide("ak", "sk"), + CredentialsProvider = new StaticCredentialsProvider("ak", "sk"), HttpTransport = new HttpTransport(mockHandler), }; using var client = new ClientImpl(config); diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs index 329be75..ecdea79 100644 --- a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs +++ b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs @@ -7,7 +7,7 @@ public class SignerV1Test [Fact] public void TestAuthHeader() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1 @@ -113,7 +113,7 @@ public void TestAuthHeader() [Fact] public void TestAuthHeaderToken() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token"); var cred = provider.GetCredentials(); //case 1 @@ -148,7 +148,7 @@ public void TestAuthHeaderToken() [Fact] public void TestAuthQuery() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1 @@ -183,7 +183,7 @@ public void TestAuthQuery() [Fact] public void TestAuthQueryToken() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token"); var cred = provider.GetCredentials(); //case 1 diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs index 542e2e0..767798b 100644 --- a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs +++ b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs @@ -7,7 +7,7 @@ public class SignerV4Test [Fact] public void TestAuthHeader() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1 @@ -64,7 +64,7 @@ public void TestAuthHeader() [Fact] public void TestAuthHeaderToken() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token"); var cred = provider.GetCredentials(); //case 1 @@ -121,7 +121,7 @@ public void TestAuthHeaderToken() [Fact] public void TestAuthHeaderWithAdditionalHeaders() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1 @@ -210,7 +210,7 @@ public void TestAuthHeaderWithAdditionalHeaders() [Fact] public void TestAuthQuery() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1 @@ -272,7 +272,7 @@ public void TestAuthQuery() [Fact] public void TestAuthQueryToken() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token"); var cred = provider.GetCredentials(); //case 1 @@ -336,7 +336,7 @@ public void TestAuthQueryToken() [Fact] public void TestAuthQueryWithAdditionalHeaders() { - var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk"); + var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk"); var cred = provider.GetCredentials(); //case 1