├── .tool-versions ├── samples ├── SwiftClient.AspNetCore.Demo │ ├── .bowerrc │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ └── Home │ │ │ └── Index.cshtml │ ├── bower.json │ ├── wwwroot │ │ ├── _references.js │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── cinema.png │ │ │ ├── app-cluster.png │ │ │ └── default-video-thumb.png │ │ ├── lib │ │ │ ├── jquery-Mustache │ │ │ │ └── .gitignore │ │ │ ├── video-js │ │ │ │ └── dist │ │ │ │ │ ├── video-js.swf │ │ │ │ │ ├── font │ │ │ │ │ ├── VideoJS.eot │ │ │ │ │ ├── VideoJS.ttf │ │ │ │ │ └── VideoJS.woff │ │ │ │ │ ├── video-js-5.1.0.zip │ │ │ │ │ ├── examples │ │ │ │ │ ├── shared │ │ │ │ │ │ └── example-captions.vtt │ │ │ │ │ └── simple-embed │ │ │ │ │ │ └── index.html │ │ │ │ │ └── lang │ │ │ │ │ ├── zh-CN.js │ │ │ │ │ ├── zh-TW.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── ba.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── pt-BR.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── fr.js │ │ │ │ │ └── es.js │ │ │ ├── bootstrap │ │ │ │ └── dist │ │ │ │ │ ├── fonts │ │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ │ └── js │ │ │ │ │ └── npm.js │ │ │ └── mustache.js │ │ │ │ └── mustache.js.nuspec │ │ ├── css │ │ │ └── site.css │ │ └── js │ │ │ └── site.js │ ├── appsettings.json │ ├── web.config │ ├── Controllers │ │ └── ErrorController.cs │ ├── Program.cs │ ├── bundleconfig.json │ ├── Properties │ │ └── launchSettings.json │ ├── Models │ │ └── ContainerViewModel.cs │ ├── README.md │ ├── Startup.cs │ └── SwiftClient.AspNetCore.Demo.csproj └── SwiftClient.Cli │ ├── Models.cs │ ├── Commands │ ├── StatsOptions.cs │ ├── ImportOptions.cs │ ├── LoginOptions.cs │ ├── ListOptions.cs │ ├── ExportOptions.cs │ ├── DeleteOptions.cs │ ├── GetOptions.cs │ ├── GetCommand.cs │ ├── StatsCommand.cs │ ├── PutOptions.cs │ ├── DeleteCommand.cs │ └── ListCommand.cs │ ├── Extensions │ ├── StringExtensions.cs │ ├── SwiftModelExtensions.cs │ ├── PathEscaper.cs │ └── TableParserExtensions.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Logger.cs │ ├── SwiftConsoleLog.cs │ ├── SwiftClient.Cli.csproj │ ├── install.sh │ ├── Program.cs │ ├── Helpers │ └── ProgressBar.cs │ ├── README.md │ └── AuthManager.cs ├── push_client.sh ├── tools └── docker-swift │ ├── down.ps1 │ ├── files │ ├── dispersion.conf │ ├── swift.conf │ ├── object-server.conf │ ├── rsyncd.conf │ ├── account-server.conf │ ├── container-server.conf │ ├── proxy-server.conf │ ├── object-expirer.conf │ ├── supervisord.conf │ └── startmain.sh │ ├── down.sh │ ├── up.sh │ ├── up.ps1 │ ├── Dockerfile │ └── README.md ├── push_service.sh ├── test └── SwiftClient.Test │ ├── appsettings.json │ ├── SwiftFixtureDemo.cs │ ├── Utils │ ├── CustomConfigReader.cs │ └── RandomBufferGenerator.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SwiftFixtureWebHost.cs │ ├── SwiftLogger.cs │ ├── SwiftClient.Test.csproj │ └── SwiftFixture.cs ├── src ├── SwiftClient │ ├── Models │ │ ├── SwiftAuthData.cs │ │ ├── SwiftContainerResponse.cs │ │ ├── SwiftAccountResponse.cs │ │ ├── SwiftBaseResponse.cs │ │ ├── SwiftContainerModel.cs │ │ ├── SwiftCredentials.cs │ │ ├── SwiftObjectModel.cs │ │ └── SwiftResponse.cs │ ├── Interfaces │ │ ├── ISwiftLogger.cs │ │ ├── ISwiftAuthManager.cs │ │ └── ISwiftClient.cs │ ├── Extensions │ │ ├── IListExtensions.cs │ │ ├── StringExtensions.cs │ │ ├── DictionaryExtensions.cs │ │ ├── SwiftResponseExtensions.cs │ │ └── HttpExtensions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── SwiftAuthManager.cs │ ├── SwiftUrlBuilder.cs │ ├── SwiftHeaderKeys.cs │ ├── SwiftClient.csproj │ ├── SwiftClientConfig.cs │ ├── SwiftClientAuthorization.cs │ ├── SwiftClient.cs │ └── RetryPolicy.cs └── SwiftClient.AspNetCore │ ├── SwiftService.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SwiftServiceOptions.cs │ ├── SwiftServiceLogger.cs │ ├── SwiftClientServiceCollectionExtensions.cs │ ├── SwiftClient.AspNetCore.csproj │ ├── SwiftAuthManagerMemoryCache.cs │ ├── SwiftAuthManagerDistributedCache.cs │ └── HttpExtensions.cs ├── NuGet.Config ├── appveyor.yml ├── Makefile ├── .gitignore ├── LICENSE.md ├── Build.ps1 ├── .gitattributes └── SwiftClient.sln /.tool-versions: -------------------------------------------------------------------------------- 1 | dotnet-core 2.1.403 2 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "wwwroot/lib" 3 | } 4 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp.net", 3 | "private": true, 4 | "dependencies": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/_references.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Models.cs: -------------------------------------------------------------------------------- 1 | using Humanizer; 2 | using System.Collections.Generic; 3 | 4 | namespace SwiftClient.Cli 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using SwiftClient.AspNetCore.Demo 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /push_client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd src/SwiftClient; dotnet nuget push -k ${NUGET_KEY} -s ${NUGET_SERVER} $(ls -dt bin/Release/* | head -1) 6 | 7 | exit 0 -------------------------------------------------------------------------------- /tools/docker-swift/down.ps1: -------------------------------------------------------------------------------- 1 | # Stop and remove swift containers and image if any 2 | docker stop SWIFT_AIO 3 | docker rm SWIFT_AIO 4 | docker rm SWIFT_DATA 5 | #docker rmi swift-aio -------------------------------------------------------------------------------- /tools/docker-swift/files/dispersion.conf: -------------------------------------------------------------------------------- 1 | [dispersion] 2 | auth_url = http://127.0.0.1:8080/auth/v1.0 3 | auth_user = test:tester 4 | auth_key = testing 5 | endpoint_type = internalURL -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/images/cinema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/images/cinema.png -------------------------------------------------------------------------------- /tools/docker-swift/files/swift.conf: -------------------------------------------------------------------------------- 1 | [swift-hash] 2 | # random unique strings that can never change (DO NOT LOSE) 3 | swift_hash_path_prefix = changeme 4 | swift_hash_path_suffix = changeme -------------------------------------------------------------------------------- /push_service.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd src/SwiftClient.AspNetCore; dotnet nuget push -k ${NUGET_KEY} -s ${NUGET_SERVER} $(ls -dt bin/Release/* | head -1) 6 | 7 | exit 0 -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/images/app-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/images/app-cluster.png -------------------------------------------------------------------------------- /tools/docker-swift/down.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Stop and remove swift containers and image if any 4 | docker stop SWIFT_AIO 5 | docker rm SWIFT_AIO 6 | docker rm SWIFT_DATA 7 | #docker rmi swift-aio 8 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/jquery-Mustache/.gitignore: -------------------------------------------------------------------------------- 1 | # IDE junk 2 | .settings 3 | .project 4 | .idea 5 | 6 | # OS Junk 7 | .DS_Store 8 | 9 | # Build artifactos 10 | dist/ 11 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/images/default-video-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/images/default-video-thumb.png -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/video-js.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/video-js.swf -------------------------------------------------------------------------------- /test/SwiftClient.Test/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "SwiftCluster": { 3 | "Username": "test:tester", 4 | "Password": "testing", 5 | "Endpoints": [ 6 | "http://localhost:8080" 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.eot -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.ttf -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/font/VideoJS.woff -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/video-js-5.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/video-js-5.1.0.zip -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftAuthData.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SwiftClient 3 | { 4 | public class SwiftAuthData 5 | { 6 | public string StorageUrl { get; set; } 7 | public string AuthToken { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristipufu/swiftclient-net/HEAD/samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | branches: 5 | only: 6 | - master 7 | nuget: 8 | disable_publish_on_pr: true 9 | build_script: 10 | - ps: .\Build.ps1 11 | test: off 12 | artifacts: 13 | - path: .\artifacts\**\*.nupkg 14 | name: NuGet 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | pack_client: 2 | cd src/SwiftClient; dotnet pack -c Release 3 | pack_service: 4 | cd src/SwiftClient.AspNetCore; dotnet pack -c Release 5 | push_client: 6 | ./push_client.sh 7 | push_service: 8 | ./push_service.sh 9 | upload_client: pack_client push_client 10 | upload_service: pack_service push_service 11 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/StatsOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("stats", HelpText = "account statistics")] 12 | public class StatsOptions 13 | { 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftContainerResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SwiftClient 4 | { 5 | public class SwiftContainerResponse : SwiftBaseResponse 6 | { 7 | public long ObjectsCount { get; set; } 8 | public long TotalBytes { get; set; } 9 | public List Objects { get; set; } 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftAccountResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SwiftClient 4 | { 5 | public class SwiftAccountResponse : SwiftBaseResponse 6 | { 7 | public long ContainersCount { get; set; } 8 | public long ObjectsCount { get; set; } 9 | public long TotalBytes { get; set; } 10 | public List Containers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SwiftClient/Interfaces/ISwiftLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace SwiftClient 5 | { 6 | public interface ISwiftLogger 7 | { 8 | void LogAuthenticationError(Exception ex, string username, string password, string endpoint); 9 | void LogRequestError(Exception ex, HttpStatusCode statusCode, string reason, string requestUrl); 10 | void LogUnauthorizedError(string token, string endpoint); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | _ReSharper.*/ 6 | packages/ 7 | artifacts/ 8 | PublishProfiles/ 9 | *.user 10 | *.suo 11 | *.cache 12 | *.docstates 13 | _ReSharper.* 14 | nuget.exe 15 | *net45.csproj 16 | *k10.csproj 17 | *.psess 18 | *.vsp 19 | *.pidb 20 | *.userprefs 21 | *DS_Store 22 | *.ncrunchsolution 23 | *.*sdf 24 | *.ipch 25 | .vs/ 26 | project.lock.json 27 | .envrc 28 | .DS_Store 29 | bower_components/ 30 | node_modules/ 31 | .idea -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "SwiftCluster": { 11 | "Username": "test:tester", 12 | "Password": "testing", 13 | "Endpoints": [ 14 | "http://localhost:8080" 15 | ], 16 | "RetryCount": 1, 17 | "RetryPerEndpointCount": 2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/SwiftClient/Extensions/IListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace SwiftClient.Extensions 4 | { 5 | internal static class IListExtensions 6 | { 7 | public static void MoveFirstToLast(this IList list) 8 | { 9 | var count = list.Count; 10 | 11 | if (count < 2) return; 12 | 13 | var item = list[0]; 14 | list.RemoveAt(0); 15 | list.Insert(count - 1, item); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftBaseResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace SwiftClient 5 | { 6 | public class SwiftBaseResponse 7 | { 8 | public HttpStatusCode StatusCode { get; set; } 9 | 10 | public string Reason { get; set; } 11 | 12 | public long ContentLength { get; set; } 13 | 14 | public bool IsSuccess { get; set; } 15 | 16 | public Dictionary Headers { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftContainerModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | using Newtonsoft.Json; 3 | 4 | namespace SwiftClient 5 | { 6 | public class SwiftContainerModel 7 | { 8 | [JsonProperty(PropertyName = "count")] 9 | public long Objects { get; set; } 10 | 11 | [JsonProperty(PropertyName = "bytes")] 12 | public long Bytes { get; set; } 13 | 14 | [JsonProperty(PropertyName = "name")] 15 | public string Container { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SwiftClient/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace SwiftClient.Extensions 5 | { 6 | internal static class StringExtensions 7 | { 8 | public static string Encode(this string stringToEncode) 9 | { 10 | var stringSplit = stringToEncode.Split('/'); 11 | var stringEncoded = stringSplit.Select(x => Uri.EscapeDataString(x)); 12 | return string.Join("/", stringEncoded); 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/SwiftFixtureDemo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace SwiftClient.Test 9 | { 10 | public class SwiftFixtureDemo : SwiftFixtureWebHost 11 | where TStartup : class 12 | { 13 | public SwiftFixtureDemo() : base("http://localhost:5000") 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tools/docker-swift/up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # Build local 6 | docker build -t swift-aio . 7 | 8 | # Build storage container 9 | docker run -v /srv --name SWIFT_DATA busybox 10 | 11 | # Start swift server 12 | ID=$(docker run --name SWIFT_AIO -d -p 8080:8080 --volumes-from SWIFT_DATA -t swift-aio) 13 | 14 | # Wait for supervisord to start 15 | sleep 10s 16 | 17 | # print swift server logs 18 | docker logs $ID 19 | 20 | # test auth 21 | curl -i -H "X-Auth-User:test:tester" -H "X-Auth-Key:testing" http://localhost:8080/auth/v1.0/ 22 | -------------------------------------------------------------------------------- /tools/docker-swift/files/object-server.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | devices = /srv 3 | mount_check = false 4 | disable_fallocate = true 5 | bind_port = 6010 6 | workers = 1 7 | user = swift 8 | log_facility = LOG_LOCAL2 9 | recon_cache_path = /var/cache/swift 10 | eventlet_debug = true 11 | 12 | [pipeline:main] 13 | pipeline = recon object-server 14 | 15 | [app:object-server] 16 | use = egg:swift#object 17 | 18 | [filter:recon] 19 | use = egg:swift#recon 20 | 21 | [object-replicator] 22 | vm_test_mode = yes 23 | 24 | [object-updater] 25 | 26 | [object-auditor] -------------------------------------------------------------------------------- /tools/docker-swift/files/rsyncd.conf: -------------------------------------------------------------------------------- 1 | uid = swift 2 | gid = swift 3 | log file = /var/log/rsyncd.log 4 | pid file = /var/run/rsyncd.pid 5 | address = 127.0.0.1 6 | 7 | [account6012] 8 | max connections = 25 9 | path = /srv/ 10 | read only = false 11 | lock file = /var/lock/account6012.lock 12 | 13 | [container6011] 14 | max connections = 25 15 | path = /srv/ 16 | read only = false 17 | lock file = /var/lock/container6011.lock 18 | 19 | [object6010] 20 | max connections = 25 21 | path = /srv/ 22 | read only = false 23 | lock file = /var/lock/object6010.lock -------------------------------------------------------------------------------- /tools/docker-swift/files/account-server.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | devices = /srv 3 | mount_check = false 4 | disable_fallocate = true 5 | bind_port = 6012 6 | workers = 1 7 | user = swift 8 | log_facility = LOG_LOCAL2 9 | recon_cache_path = /var/cache/swift 10 | eventlet_debug = true 11 | 12 | [pipeline:main] 13 | pipeline = recon account-server 14 | 15 | [app:account-server] 16 | use = egg:swift#account 17 | 18 | [filter:recon] 19 | use = egg:swift#recon 20 | 21 | [account-replicator] 22 | vm_test_mode = yes 23 | 24 | [account-auditor] 25 | 26 | [account-reaper] -------------------------------------------------------------------------------- /src/SwiftClient/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace SwiftClient.Extensions 5 | { 6 | internal static class DictionaryExtensions 7 | { 8 | public static string ToQueryString(this Dictionary dict) 9 | { 10 | var array = (from key in dict.Keys 11 | select string.Format("{0}={1}", key, dict[key])) 12 | .ToArray(); 13 | return "?" + string.Join("&", array); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /tools/docker-swift/up.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | # Build local 4 | docker build -t swift-aio . 5 | 6 | # Build storage container 7 | docker run -v /srv --name SWIFT_DATA busybox 8 | 9 | # Start swift server 10 | $ID = docker run --name SWIFT_AIO -d -p 8080:8080 --volumes-from SWIFT_DATA -t swift-aio 11 | 12 | # Wait for supervisord to start 13 | Start-Sleep -Seconds 15 14 | 15 | # print swift server logs 16 | docker logs $ID 17 | 18 | # test auth 19 | Invoke-RestMethod -Method Get -Headers @{'X-Auth-User'= 'test:tester';'X-Auth-Key'='testing'} -Uri http://localhost:8080/auth/v1.0/ -------------------------------------------------------------------------------- /tools/docker-swift/files/container-server.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | devices = /srv 3 | mount_check = false 4 | disable_fallocate = true 5 | bind_port = 6011 6 | workers = 1 7 | user = swift 8 | log_facility = LOG_LOCAL2 9 | recon_cache_path = /var/cache/swift 10 | eventlet_debug = true 11 | allow_versions = true 12 | 13 | [pipeline:main] 14 | pipeline = recon container-server 15 | 16 | [app:container-server] 17 | use = egg:swift#container 18 | 19 | [filter:recon] 20 | use = egg:swift#recon 21 | 22 | [container-replicator] 23 | vm_test_mode = yes 24 | 25 | [container-updater] 26 | 27 | [container-auditor] 28 | 29 | [container-sync] -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/mustache.js/mustache.js.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mustache.js 5 | 2.2.0 6 | mustache.js Authors 7 | https://github.com/janl/mustache.js/blob/master/LICENSE 8 | http://mustache.github.com/ 9 | false 10 | Logic-less templates in JavaScript. 11 | 12 | mustache template templates javascript 13 | 14 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftCredentials.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Collections.Generic; 3 | 4 | namespace SwiftClient 5 | { 6 | public class SwiftCredentials 7 | { 8 | /// 9 | /// List of proxy endpoints 10 | /// 11 | public List Endpoints { get; set; } 12 | 13 | /// 14 | /// Format ":", eg: "system:root" 15 | /// 16 | public string Username { get; set; } 17 | 18 | /// 19 | /// Account user password 20 | /// 21 | public string Password { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/ImportOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("import", HelpText = "import containers with objects to account")] 12 | public class ImportOptions 13 | { 14 | [Option('c', "container", Required = false, HelpText = "container name")] 15 | public string Container { get; set; } 16 | 17 | [Option('p', "path", Required = false, HelpText = "path")] 18 | public string Path { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Controllers/ErrorController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace SwiftClient.AspNetCore.Demo.Controllers 4 | { 5 | public class ErrorController : Controller 6 | { 7 | public ErrorController() 8 | { 9 | 10 | } 11 | 12 | public IActionResult Http403() 13 | { 14 | return View("Forbidden"); 15 | } 16 | 17 | public IActionResult Http404() 18 | { 19 | return View("NotFound"); 20 | } 21 | 22 | public IActionResult Http500() 23 | { 24 | return View("ServerError"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | 8 | namespace SwiftClient.AspNetCore.Demo 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | var host = new WebHostBuilder() 15 | .UseKestrel() 16 | .UseContentRoot(Directory.GetCurrentDirectory()) 17 | .UseIISIntegration() 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftObjectModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace SwiftClient 5 | { 6 | public class SwiftObjectModel 7 | { 8 | [JsonProperty(PropertyName = "hash")] 9 | public string Hash { get; set; } 10 | 11 | [JsonProperty(PropertyName = "last_modified")] 12 | public DateTime LastModified { get; set; } 13 | 14 | [JsonProperty(PropertyName = "bytes")] 15 | public long Bytes { get; set; } 16 | 17 | [JsonProperty(PropertyName = "name")] 18 | public string Object { get; set; } 19 | 20 | [JsonProperty(PropertyName = "content_type")] 21 | public string ContentType { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/Utils/CustomConfigReader.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Options; 4 | 5 | namespace SwiftClient.Test 6 | { 7 | public static class CustomConfigReader 8 | { 9 | public static T Get(IConfiguration section) where T : class, new() 10 | { 11 | var services = new ServiceCollection(); 12 | services.Configure(section); 13 | 14 | T obj = new T(); 15 | 16 | (services[0].ImplementationInstance as ConfigureFromConfigurationOptions).Action.Invoke(obj); 17 | 18 | return obj; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optinally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/LoginOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("login", HelpText = "swift login")] 12 | public class LoginOptions 13 | { 14 | [Option('h', "host", Required = true, HelpText = "swift proxy URL")] 15 | public string Endpoint { get; set; } 16 | 17 | [Option('u', "user", Required = true, HelpText = "username")] 18 | public string User { get; set; } 19 | 20 | [Option('p', "password", Required = true, HelpText = "password")] 21 | public string Password { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49134/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SwiftClient.AspNetCore.Demo": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "launchUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 |

Development Mode

9 |

10 | Swapping to Development environment will display more detailed information about the error that occurred. 11 |

12 |

13 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 14 |

15 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/ListOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("ls", HelpText = "list containers or objects in container if a container is specified")] 12 | public class ListOptions 13 | { 14 | [Option('c', "container", Required = false, HelpText = "container name")] 15 | public string Container { get; set; } 16 | 17 | [Option('d', "delimiter", Required = false, HelpText = "delimiter")] 18 | public string Delimiter { get; set; } 19 | 20 | [Option('p', "prefix", Required = false, HelpText = "prefix")] 21 | public string Prefix { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Models/ContainerViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SwiftClient.AspNetCore.Demo 4 | { 5 | public class PageViewModel 6 | { 7 | public TreeViewModel Tree { get; set; } 8 | 9 | public string Message { get; set; } 10 | } 11 | 12 | public class TreeViewModel 13 | { 14 | public string text { get; set; } 15 | 16 | public string containerId { get; set; } 17 | 18 | public string objectId { get; set; } 19 | 20 | public bool isExpandable { get; set; } 21 | 22 | public bool isFile { get; set; } 23 | 24 | public bool isVideo { get; set; } 25 | 26 | public bool hasNodes { get; set; } 27 | 28 | public List nodes { get; set; } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/ExportOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("export", HelpText = "export all objects for account or objects in container if a container is specified")] 12 | public class ExportOptions 13 | { 14 | [Option('c', "container", Required = false, HelpText = "container name")] 15 | public string Container { get; set; } 16 | 17 | [Option("prefix", Required = false, HelpText = "prefix")] 18 | public string Prefix { get; set; } 19 | 20 | [Option('p', "path", Required = false, HelpText = "path")] 21 | public string Path { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SwiftClient.Cli 7 | { 8 | public static class StringExtensions 9 | { 10 | public static string[] ParseArguments(this string commandLine) 11 | { 12 | char[] parmChars = commandLine.ToCharArray(); 13 | bool inQuote = false; 14 | for (int index = 0; index < parmChars.Length; index++) 15 | { 16 | if (parmChars[index] == '"') 17 | inQuote = !inQuote; 18 | if (!inQuote && parmChars[index] == ' ') 19 | parmChars[index] = '\n'; 20 | } 21 | return (new string(parmChars)).Split('\n'); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/DeleteOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("rm", HelpText = "remove container and it's content or a single object if one is specified")] 12 | public class DeleteOptions 13 | { 14 | [Option('c', "container", Required = true, HelpText = "container")] 15 | public string Container { get; set; } 16 | 17 | [Option('o', "object", Required = false, HelpText = "object")] 18 | public string Object { get; set; } 19 | 20 | [Option('l', "limit", Required = false, Default = 1000, HelpText = "number of objects to delete in one call")] 21 | public int Limit { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SwiftClient/Models/SwiftResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SwiftClient 5 | { 6 | public class SwiftResponse : SwiftBaseResponse, IDisposable 7 | { 8 | bool disposed = false; 9 | 10 | /// 11 | /// Reference to network stream 12 | /// 13 | public Stream Stream { get; set; } 14 | 15 | public void Dispose() 16 | { 17 | Dispose(true); 18 | GC.SuppressFinalize(this); 19 | } 20 | 21 | protected virtual void Dispose(bool disposing) 22 | { 23 | if (disposed) 24 | return; 25 | 26 | if (disposing) 27 | { 28 | if (Stream != null) Stream.Dispose(); 29 | } 30 | 31 | disposed = true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Extensions/SwiftModelExtensions.cs: -------------------------------------------------------------------------------- 1 | using Humanizer; 2 | 3 | namespace SwiftClient.Cli 4 | { 5 | public class SwiftObject : SwiftObjectModel 6 | { 7 | public string Size 8 | { 9 | get 10 | { 11 | return Bytes.Bytes().Humanize("0.00"); 12 | } 13 | } 14 | } 15 | 16 | public class SwiftContainer : SwiftContainerModel 17 | { 18 | public string Size 19 | { 20 | get 21 | { 22 | return Bytes.Bytes().Humanize("0.00"); 23 | } 24 | } 25 | } 26 | 27 | public class SwiftAccountStats : SwiftAccountResponse 28 | { 29 | public string Size 30 | { 31 | get 32 | { 33 | return TotalBytes.Bytes().Humanize("0.00"); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/GetOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("get", HelpText = "download file")] 12 | public class GetOptions 13 | { 14 | [Option('c', "container", Required = true, HelpText = "container")] 15 | public string Container { get; set; } 16 | 17 | [Option('o', "object", Required = true, HelpText = "object")] 18 | public string Object { get; set; } 19 | 20 | [Option('f', "file", Required = true, HelpText = "destination file path")] 21 | public string File { get; set; } 22 | 23 | [Option('b', "buffer", Required = false, Default = 2, HelpText = "buffer size in MB, default is 2MB")] 24 | public int BufferSize { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/Utils/RandomBufferGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SwiftClient.Test 4 | { 5 | public class RandomBufferGenerator 6 | { 7 | private readonly Random _random = new Random(); 8 | private readonly byte[] _seedBuffer; 9 | 10 | public RandomBufferGenerator(int maxBufferSize) 11 | { 12 | _seedBuffer = new byte[maxBufferSize]; 13 | 14 | _random.NextBytes(_seedBuffer); 15 | } 16 | 17 | public byte[] GenerateBufferFromSeed(int size) 18 | { 19 | int randomWindow = _random.Next(0, size); 20 | 21 | byte[] buffer = new byte[size]; 22 | 23 | Buffer.BlockCopy(_seedBuffer, randomWindow, buffer, 0, size - randomWindow); 24 | Buffer.BlockCopy(_seedBuffer, 0, buffer, size - randomWindow, randomWindow); 25 | 26 | return buffer; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/SwiftClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("SwiftClient")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("c855aa03-fd6c-422f-99fa-4a0bfcb67f39")] 20 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("SwiftClient.Cli")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("ba84d7d5-c30d-4097-a4d3-7ba574db13bf")] 20 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("SwiftClient.Test")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("5985b113-1a1c-4efd-b06c-341c4b9467e9")] 20 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace SwiftClient.AspNetCore 10 | { 11 | public class SwiftService : Client 12 | { 13 | private readonly SwiftServiceOptions _options; 14 | 15 | public SwiftService(IOptions options, 16 | ISwiftAuthManager authManager, 17 | IHttpClientFactory httpClientFactory, 18 | ISwiftLogger logger): base(authManager, logger) 19 | { 20 | _options = options.Value; 21 | SetRetryCount(_options.RetryCount); 22 | SetHttpClient(httpClientFactory, _options.NoHttpDispose); 23 | SetRetryPerEndpointCount(_options.RetryPerEndpointCount); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("SwiftClient.AspNetCore")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("ef6aad2d-f8c5-491d-87f2-7f24733163cf")] 20 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/examples/shared/example-captions.vtt: -------------------------------------------------------------------------------- 1 | WEBVTT 2 | 3 | 00:00.700 --> 00:04.110 4 | Captions describe all relevant audio for the hearing impaired. 5 | [ Heroic music playing for a seagull ] 6 | 7 | 00:04.500 --> 00:05.000 8 | [ Splash!!! ] 9 | 10 | 00:05.100 --> 00:06.000 11 | [ Sploosh!!! ] 12 | 13 | 00:08.000 --> 00:09.225 14 | [ Splash...splash...splash splash splash ] 15 | 16 | 00:10.525 --> 00:11.255 17 | [ Splash, Sploosh again ] 18 | 19 | 00:13.500 --> 00:14.984 20 | Dolphin: eeeEEEEEeeee! 21 | 22 | 00:14.984 --> 00:16.984 23 | Dolphin: Squawk! eeeEEE? 24 | 25 | 00:25.000 --> 00:28.284 26 | [ A whole ton of splashes ] 27 | 28 | 00:29.500 --> 00:31.000 29 | Mine. Mine. Mine. 30 | 31 | 00:34.300 --> 00:36.000 32 | Shark: Chomp 33 | 34 | 00:36.800 --> 00:37.900 35 | Shark: CHOMP!!! 36 | 37 | 00:37.861 --> 00:41.193 38 | EEEEEEOOOOOOOOOOWHALENOISE 39 | 40 | 00:42.593 --> 00:45.611 41 | [ BIG SPLASH ] -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SwiftClient.Cli 7 | { 8 | public static class Logger 9 | { 10 | public static void Log(string value) 11 | { 12 | Console.ResetColor(); 13 | Console.WriteLine(value); 14 | } 15 | 16 | public static void LogWarning(string value) 17 | { 18 | Console.ForegroundColor = ConsoleColor.DarkYellow; 19 | Console.WriteLine(value); 20 | Console.ResetColor(); 21 | } 22 | 23 | public static void LogError(string value) 24 | { 25 | Console.ForegroundColor = ConsoleColor.Red; 26 | //TODO: determine if the current cursor is on a new line or not 27 | Console.Write(Environment.NewLine); 28 | Console.WriteLine(value); 29 | Console.ResetColor(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/SwiftFixtureWebHost.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.AspNetCore.TestHost; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace SwiftClient.Test 10 | { 11 | public class SwiftFixtureWebHost : SwiftFixture 12 | where TStartup : class 13 | { 14 | private readonly TestServer _server; 15 | 16 | public SwiftFixtureWebHost(string baseUri) : base() 17 | { 18 | var builder = new WebHostBuilder().UseStartup(); 19 | _server = new TestServer(builder); 20 | 21 | HttpClient = _server.CreateClient(); 22 | HttpClient.BaseAddress = new Uri(baseUri); 23 | } 24 | 25 | public HttpClient HttpClient { get; } 26 | 27 | public override void Dispose() 28 | { 29 | HttpClient.Dispose(); 30 | _server.Dispose(); 31 | 32 | base.Dispose(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/SwiftClient/Extensions/SwiftResponseExtensions.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SwiftClient 3 | { 4 | public static class SwiftResponseExtensions 5 | { 6 | public static string GetMeta(this SwiftBaseResponse rsp, string metaName) 7 | { 8 | if (rsp.Headers == null) return null; 9 | 10 | var objectMetaKey = string.Format(SwiftHeaderKeys.ObjectMetaFormat, metaName); 11 | 12 | if (rsp.Headers.ContainsKey(objectMetaKey)) 13 | { 14 | return rsp.Headers[objectMetaKey]; 15 | } 16 | 17 | var containerMetaKey = string.Format(SwiftHeaderKeys.ContainerMetaFormat, metaName); 18 | 19 | if (rsp.Headers.ContainsKey(containerMetaKey)) 20 | { 21 | return rsp.Headers[containerMetaKey]; 22 | } 23 | 24 | var accountMetaKey = string.Format(SwiftHeaderKeys.AccountMetaFormat, metaName); 25 | 26 | if (rsp.Headers.ContainsKey(accountMetaKey)) 27 | { 28 | return rsp.Headers[accountMetaKey]; 29 | } 30 | 31 | return null; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Stefan Prodan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Extensions/PathEscaper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | 7 | namespace SwiftClient.Cli 8 | { 9 | public static class PathEscaper 10 | { 11 | static readonly string invalidChars = @"""\/?:<>*|"; 12 | static readonly string escapeChar = "%"; 13 | 14 | static readonly Regex escaper = new Regex( 15 | "[" + Regex.Escape(escapeChar + invalidChars) + "]", 16 | RegexOptions.Compiled); 17 | static readonly Regex unescaper = new Regex( 18 | Regex.Escape(escapeChar) + "([0-9A-Z]{4})", 19 | RegexOptions.Compiled); 20 | 21 | public static string Escape(string path) 22 | { 23 | return escaper.Replace(path, 24 | m => escapeChar + ((short)(m.Value[0])).ToString("X4")); 25 | } 26 | 27 | public static string Unescape(string path) 28 | { 29 | return unescaper.Replace(path, 30 | m => ((char)Convert.ToInt16(m.Groups[1].Value, 16)).ToString()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftServiceOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | 4 | namespace SwiftClient.AspNetCore 5 | { 6 | public class SwiftServiceOptions 7 | { 8 | /// 9 | /// List of proxy endpoints 10 | /// 11 | public List Endpoints { get; set; } 12 | 13 | /// 14 | /// Format ":", eg: "system:root" 15 | /// 16 | public string Username { get; set; } 17 | 18 | /// 19 | /// Account user password 20 | /// 21 | public string Password { get; set; } 22 | 23 | /// 24 | /// Set retries count for all proxy nodes 25 | /// 26 | public int RetryCount { get; set; } = 1; 27 | 28 | /// 29 | /// Disables disposing httpclient 30 | /// 31 | public bool NoHttpDispose { get; set; } = true; 32 | 33 | /// 34 | /// Set retries count per proxy node request 35 | /// 36 | public int RetryPerEndpointCount { get; set; } = 1; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/zh-CN.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("zh-CN",{ 2 | "Play": "播放", 3 | "Pause": "暂停", 4 | "Current Time": "当前时间", 5 | "Duration Time": "时长", 6 | "Remaining Time": "剩余时间", 7 | "Stream Type": "媒体流类型", 8 | "LIVE": "直播", 9 | "Loaded": "加载完毕", 10 | "Progress": "进度", 11 | "Fullscreen": "全屏", 12 | "Non-Fullscreen": "退出全屏", 13 | "Mute": "静音", 14 | "Unmuted": "取消静音", 15 | "Playback Rate": "播放码率", 16 | "Subtitles": "字幕", 17 | "subtitles off": "字幕关闭", 18 | "Captions": "内嵌字幕", 19 | "captions off": "内嵌字幕关闭", 20 | "Chapters": "节目段落", 21 | "You aborted the media playback": "视频播放被终止", 22 | "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。", 25 | "No compatible source was found for this media.": "无法找到此视频兼容的源。", 26 | "The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。" 27 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/zh-TW.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("zh-TW",{ 2 | "Play": "播放", 3 | "Pause": "暫停", 4 | "Current Time": "目前時間", 5 | "Duration Time": "總共時間", 6 | "Remaining Time": "剩餘時間", 7 | "Stream Type": "串流類型", 8 | "LIVE": "直播", 9 | "Loaded": "載入完畢", 10 | "Progress": "進度", 11 | "Fullscreen": "全螢幕", 12 | "Non-Fullscreen": "退出全螢幕", 13 | "Mute": "靜音", 14 | "Unmuted": "取消靜音", 15 | "Playback Rate": " 播放速率", 16 | "Subtitles": "字幕", 17 | "subtitles off": "關閉字幕", 18 | "Captions": "內嵌字幕", 19 | "captions off": "關閉內嵌字幕", 20 | "Chapters": "章節", 21 | "You aborted the media playback": "影片播放已終止", 22 | "A network error caused the media download to fail part-way.": "網路錯誤導致影片下載失敗。", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "影片因格式不支援或者伺服器或網路的問題無法載入。", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由於影片檔案損毀或是該影片使用了您的瀏覽器不支援的功能,播放終止。", 25 | "No compatible source was found for this media.": "無法找到相容此影片的來源。", 26 | "The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。" 27 | }); -------------------------------------------------------------------------------- /src/SwiftClient/SwiftAuthManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace SwiftClient 6 | { 7 | public class SwiftAuthManager : ISwiftAuthManager 8 | { 9 | SwiftAuthData _authData; 10 | List _endpoints; 11 | 12 | public Func> Authenticate { get; set; } 13 | 14 | public SwiftCredentials Credentials { get; set; } 15 | 16 | public SwiftAuthManager() { } 17 | 18 | public SwiftAuthManager(SwiftCredentials credentials) 19 | { 20 | Credentials = credentials; 21 | } 22 | 23 | public SwiftAuthData GetAuthData() 24 | { 25 | return _authData; 26 | } 27 | 28 | public List GetEndpoints() 29 | { 30 | return _endpoints ?? Credentials.Endpoints; 31 | } 32 | 33 | public void SetAuthData(SwiftAuthData authData) 34 | { 35 | _authData = authData; 36 | } 37 | 38 | public void SetEndpoints(List endpoints) 39 | { 40 | _endpoints = endpoints; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ko.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ko",{ 2 | "Play": "재생", 3 | "Pause": "일시중지", 4 | "Current Time": "현재 시간", 5 | "Duration Time": "지정 기간", 6 | "Remaining Time": "남은 시간", 7 | "Stream Type": "스트리밍 유형", 8 | "LIVE": "라이브", 9 | "Loaded": "로드됨", 10 | "Progress": "진행", 11 | "Fullscreen": "전체 화면", 12 | "Non-Fullscreen": "전체 화면 해제", 13 | "Mute": "음소거", 14 | "Unmuted": "음소거 해제", 15 | "Playback Rate": "재생 비율", 16 | "Subtitles": "서브타이틀", 17 | "subtitles off": "서브타이틀 끄기", 18 | "Captions": "자막", 19 | "captions off": "자막 끄기", 20 | "Chapters": "챕터", 21 | "You aborted the media playback": "비디오 재생을 취소했습니다.", 22 | "A network error caused the media download to fail part-way.": "네트워크 오류로 인하여 비디오 일부를 다운로드하지 못 했습니다.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "비디오를 로드할 수 없습니다. 서버 혹은 네트워크 오류 때문이거나 지원되지 않는 형식 때문일 수 있습니다.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "비디오 재생이 취소됐습니다. 비디오가 손상되었거나 비디오가 사용하는 기능을 브라우저에서 지원하지 않는 것 같습니다.", 25 | "No compatible source was found for this media.": "비디오에 호환되지 않는 소스가 있습니다." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ja.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ja",{ 2 | "Play": "再生", 3 | "Pause": "一時停止", 4 | "Current Time": "現在の時間", 5 | "Duration Time": "長さ", 6 | "Remaining Time": "残りの時間", 7 | "Stream Type": "ストリームの種類", 8 | "LIVE": "ライブ", 9 | "Loaded": "ロード済み", 10 | "Progress": "進行状況", 11 | "Fullscreen": "フルスクリーン", 12 | "Non-Fullscreen": "フルスクリーン以外", 13 | "Mute": "ミュート", 14 | "Unmuted": "ミュート解除", 15 | "Playback Rate": "再生レート", 16 | "Subtitles": "サブタイトル", 17 | "subtitles off": "サブタイトル オフ", 18 | "Captions": "キャプション", 19 | "captions off": "キャプション オフ", 20 | "Chapters": "チャプター", 21 | "You aborted the media playback": "動画再生を中止しました", 22 | "A network error caused the media download to fail part-way.": "ネットワーク エラーにより動画のダウンロードが途中で失敗しました", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "サーバーまたはネットワークのエラー、またはフォーマットがサポートされていないため、動画をロードできませんでした", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "破損の問題、またはお使いのブラウザがサポートしていない機能が動画に使用されていたため、動画の再生が中止されました", 25 | "No compatible source was found for this media.": "この動画に対して互換性のあるソースが見つかりませんでした" 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/GetCommand.cs: -------------------------------------------------------------------------------- 1 | using Humanizer.Bytes; 2 | using System; 3 | using System.IO; 4 | 5 | namespace SwiftClient.Cli 6 | { 7 | public static class GetCommand 8 | { 9 | public static int Run(GetOptions options, Client client) 10 | { 11 | int bufferSize = Convert.ToInt32(ByteSize.FromMegabytes(options.BufferSize).Bytes); 12 | var headObject = client.HeadObject(options.Container, options.Object).Result; 13 | 14 | if (headObject.IsSuccess) 15 | { 16 | using (var response = client.GetObject(options.Container, options.Object).Result) 17 | { 18 | using (Stream streamToWriteTo = File.OpenWrite(options.File)) 19 | { 20 | response.Stream.CopyTo(streamToWriteTo, bufferSize); 21 | } 22 | } 23 | 24 | Console.WriteLine($"{options.Container}/{options.Object} downloaded to {options.File} "); 25 | return 0; 26 | } 27 | else 28 | { 29 | Logger.LogError(headObject.Reason); 30 | return 404; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/StatsCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SwiftClient.Cli 7 | { 8 | public static class StatsCommand 9 | { 10 | public static int Run(StatsOptions options, Client client) 11 | { 12 | var accountData = client.GetAccount().Result; 13 | if (accountData.IsSuccess) 14 | { 15 | 16 | var stats = new SwiftAccountStats 17 | { 18 | ContainersCount = accountData.ContainersCount, 19 | ObjectsCount = accountData.ObjectsCount, 20 | TotalBytes = accountData.TotalBytes, 21 | }; 22 | var table = new List { stats }.ToStringTable( 23 | u => u.ContainersCount, 24 | u => u.ObjectsCount, 25 | u => u.Size 26 | ); 27 | Console.WriteLine(table); 28 | } 29 | else 30 | { 31 | Logger.LogError(accountData.Reason); 32 | } 33 | 34 | return 0; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/SwiftConsoleLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace SwiftClient.Cli 6 | { 7 | public class SwiftConsoleLog : ISwiftLogger 8 | { 9 | private string _authError = "Exception occured: {0} for credentials {1} : {2} on proxy node {3}"; 10 | private string _requestError = "Exception occured: {0} with status code: {1} for request url: {2}"; 11 | private string _unauthorizedError = "Unauthorized request with old token {0}"; 12 | 13 | public void LogAuthenticationError(Exception ex, string username, string password, string endpoint) 14 | { 15 | Logger.LogError(string.Format(_authError, ex.InnerException != null ? ex.InnerException.Message : ex.Message, username, password, endpoint)); 16 | } 17 | 18 | public void LogRequestError(Exception ex, HttpStatusCode statusCode, string reason, string requestUrl) 19 | { 20 | Logger.LogError(string.Format(_requestError, reason, statusCode.ToString(), requestUrl)); 21 | } 22 | 23 | public void LogUnauthorizedError(string token, string endpoint) 24 | { 25 | Logger.LogError(string.Format(_unauthorizedError, token)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/PutOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using CommandLine; 7 | using CommandLine.Text; 8 | 9 | namespace SwiftClient.Cli 10 | { 11 | [Verb("put", HelpText = "upload file")] 12 | public class PutOptions 13 | { 14 | [Option('c', "container", Required = true, HelpText = "swift container id")] 15 | public string Container { get; set; } 16 | 17 | [Option('o', "object", Required = false, HelpText = "swift object id, required only on single file upload")] 18 | public string Object { get; set; } 19 | 20 | [Option('f', "file", Required = true, HelpText = "input file or directory to be uploaded")] 21 | public string File { get; set; } 22 | 23 | [Option('b', "buffer", Required = false, Default = 2, HelpText = "buffer size in MB, default is 2MB")] 24 | public int BufferSize { get; set; } 25 | 26 | [Option('p', "parallel", Required = false, Default = 4, HelpText = "max degree of parallelism, default is 4")] 27 | public int Parallel { get; set; } 28 | 29 | [Option('l', "lower", Required = false, Default = false, HelpText = "apply ToLowerInvariant on container and object names")] 30 | public bool ToLower { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/tr.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("tr",{ 2 | "Play": "Oynat", 3 | "Pause": "Duraklat", 4 | "Current Time": "Süre", 5 | "Duration Time": "Toplam Süre", 6 | "Remaining Time": "Kalan Süre", 7 | "Stream Type": "Yayın Tipi", 8 | "LIVE": "CANLI", 9 | "Loaded": "Yüklendi", 10 | "Progress": "Yükleniyor", 11 | "Fullscreen": "Tam Ekran", 12 | "Non-Fullscreen": "Küçük Ekran", 13 | "Mute": "Ses Kapa", 14 | "Unmuted": "Ses Aç", 15 | "Playback Rate": "Oynatma Hızı", 16 | "Subtitles": "Altyazı", 17 | "subtitles off": "Altyazı Kapat", 18 | "Captions": "Ek Açıklamalar", 19 | "captions off": "Ek Açıklamalar Kapalı", 20 | "Chapters": "Bölümler", 21 | "You aborted the media playback": "Video oynatmayı iptal ettiniz", 22 | "A network error caused the media download to fail part-way.": "Video indirilirken bağlantı sorunu oluştu.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video oynatılamadı, ağ ya da sunucu hatası veya belirtilen format desteklenmiyor.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Tarayıcınız desteklemediği için videoda hata oluştu.", 25 | "No compatible source was found for this media.": "Video için kaynak bulunamadı." 26 | }); -------------------------------------------------------------------------------- /tools/docker-swift/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Stefan Prodan 3 | 4 | RUN apt-get update 5 | RUN apt-get install -y supervisor swift python-swiftclient rsync \ 6 | swift-proxy swift-object memcached python-keystoneclient \ 7 | python-swiftclient swift-plugin-s3 python-netifaces \ 8 | python-xattr python-memcache \ 9 | swift-account swift-container swift-object pwgen 10 | 11 | RUN mkdir -p /var/log/supervisor 12 | ADD files/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 13 | 14 | # 15 | # Swift configuration 16 | # - Partially fom http://docs.openstack.org/developer/swift/development_saio.html 17 | # 18 | 19 | # not sure how valuable dispersion will be... 20 | ADD files/dispersion.conf /etc/swift/dispersion.conf 21 | ADD files/rsyncd.conf /etc/rsyncd.conf 22 | ADD files/swift.conf /etc/swift/swift.conf 23 | ADD files/proxy-server.conf /etc/swift/proxy-server.conf 24 | ADD files/account-server.conf /etc/swift/account-server.conf 25 | ADD files/object-server.conf /etc/swift/object-server.conf 26 | ADD files/container-server.conf /etc/swift/container-server.conf 27 | ADD files/proxy-server.conf /etc/swift/proxy-server.conf 28 | ADD files/startmain.sh /usr/local/bin/startmain.sh 29 | RUN chmod 755 /usr/local/bin/*.sh 30 | 31 | EXPOSE 8080 32 | 33 | CMD /usr/local/bin/startmain.sh 34 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/cs.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("cs",{ 2 | "Play": "Přehrát", 3 | "Pause": "Pauza", 4 | "Current Time": "Aktuální čas", 5 | "Duration Time": "Doba trvání", 6 | "Remaining Time": "Zbývající čas", 7 | "Stream Type": "Stream Type", 8 | "LIVE": "ŽIVĚ", 9 | "Loaded": "Načteno", 10 | "Progress": "Stav", 11 | "Fullscreen": "Celá obrazovka", 12 | "Non-Fullscreen": "Zmenšená obrazovka", 13 | "Mute": "Ztlumit zvuk", 14 | "Unmuted": "Přehrát zvuk", 15 | "Playback Rate": "Rychlost přehrávání", 16 | "Subtitles": "Titulky", 17 | "subtitles off": "Titulky vypnuty", 18 | "Captions": "Popisky", 19 | "captions off": "Popisky vypnuty", 20 | "Chapters": "Kapitoly", 21 | "You aborted the media playback": "Přehrávání videa je přerušeno.", 22 | "A network error caused the media download to fail part-way.": "Video nemohlo být načteno, kvůli chybě v síti.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video nemohlo být načteno, buď kvůli chybě serveru nebo sítě nebo proto, že daný formát není podporován.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Váš prohlížeč nepodporuje formát videa.", 25 | "No compatible source was found for this media.": "Špatně zadaný zdroj videa." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ar.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ar",{ 2 | "Play": "تشغيل", 3 | "Pause": "ايقاف", 4 | "Current Time": "الوقت الحالي", 5 | "Duration Time": "Dauer", 6 | "Remaining Time": "الوقت المتبقي", 7 | "Stream Type": "نوع التيار", 8 | "LIVE": "مباشر", 9 | "Loaded": "تم التحميل", 10 | "Progress": "التقدم", 11 | "Fullscreen": "ملء الشاشة", 12 | "Non-Fullscreen": "غير ملء الشاشة", 13 | "Mute": "صامت", 14 | "Unmuted": "غير الصامت", 15 | "Playback Rate": "معدل التشغيل", 16 | "Subtitles": "الترجمة", 17 | "subtitles off": "ايقاف الترجمة", 18 | "Captions": "التعليقات", 19 | "captions off": "ايقاف التعليقات", 20 | "Chapters": "فصول", 21 | "You aborted the media playback": "لقد ألغيت تشغيل الفيديو", 22 | "A network error caused the media download to fail part-way.": "تسبب خطأ في الشبكة بفشل تحميل الفيديو بالكامل.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "لا يمكن تحميل الفيديو بسبب فشل في الخادم أو الشبكة ، أو فشل بسبب عدم امكانية قراءة تنسيق الفيديو.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "تم ايقاف تشغيل الفيديو بسبب مشكلة فساد أو لأن الفيديو المستخدم يستخدم ميزات غير مدعومة من متصفحك.", 25 | "No compatible source was found for this media.": "فشل العثور على أي مصدر متوافق مع هذا الفيديو." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/vi.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("vi",{ 2 | "Play": "Phát", 3 | "Pause": "Tạm dừng", 4 | "Current Time": "Thời gian hiện tại", 5 | "Duration Time": "Độ dài", 6 | "Remaining Time": "Thời gian còn lại", 7 | "Stream Type": "Kiểu Stream", 8 | "LIVE": "TRỰC TIẾP", 9 | "Loaded": "Đã tải", 10 | "Progress": "Tiến trình", 11 | "Fullscreen": "Toàn màn hình", 12 | "Non-Fullscreen": "Thoát toàn màn hình", 13 | "Mute": "Tắt tiếng", 14 | "Unmuted": "Bật âm thanh", 15 | "Playback Rate": "Tốc độ phát", 16 | "Subtitles": "Phụ đề", 17 | "subtitles off": "Tắt phụ đề", 18 | "Captions": "Chú thích", 19 | "captions off": "Tắt chú thích", 20 | "Chapters": "Chương", 21 | "You aborted the media playback": "Bạn đã hủy việc phát media.", 22 | "A network error caused the media download to fail part-way.": "Một lỗi mạng dẫn đến việc tải media bị lỗi.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video không tải được, mạng hay server có lỗi hoặc định dạng không được hỗ trợ.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Phát media đã bị hủy do một sai lỗi hoặc media sử dụng những tính năng trình duyệt không hỗ trợ.", 25 | "No compatible source was found for this media.": "Không có nguồn tương thích cho media này." 26 | }); -------------------------------------------------------------------------------- /src/SwiftClient/Interfaces/ISwiftAuthManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace SwiftClient 6 | { 7 | public interface ISwiftAuthManager 8 | { 9 | SwiftCredentials Credentials { get; set; } 10 | Func> Authenticate { get; set; } 11 | 12 | /// 13 | /// Use for caching the authentication token 14 | /// If you don't cache the authentication token, each swift call will be preceded by an auth call 15 | /// to obtain the token 16 | /// 17 | /// 18 | void SetAuthData(SwiftAuthData authData); 19 | 20 | /// 21 | /// Get authentication token from cache 22 | /// 23 | /// 24 | SwiftAuthData GetAuthData(); 25 | 26 | /// 27 | /// Get cached proxy endpoints (ordered by priority) 28 | /// If you don't cache the list, each swift call will try the proxy nodes in the initial priority order 29 | /// 30 | /// 31 | List GetEndpoints(); 32 | 33 | /// 34 | /// Save new endpoints order in cache 35 | /// 36 | /// 37 | void SetEndpoints(List endpoints); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ba.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ba",{ 2 | "Play": "Pusti", 3 | "Pause": "Pauza", 4 | "Current Time": "Trenutno vrijeme", 5 | "Duration Time": "Vrijeme trajanja", 6 | "Remaining Time": "Preostalo vrijeme", 7 | "Stream Type": "Način strimovanja", 8 | "LIVE": "UŽIVO", 9 | "Loaded": "Učitan", 10 | "Progress": "Progres", 11 | "Fullscreen": "Puni ekran", 12 | "Non-Fullscreen": "Mali ekran", 13 | "Mute": "Prigušen", 14 | "Unmuted": "Ne-prigušen", 15 | "Playback Rate": "Stopa reprodukcije", 16 | "Subtitles": "Podnaslov", 17 | "subtitles off": "Podnaslov deaktiviran", 18 | "Captions": "Titlovi", 19 | "captions off": "Titlovi deaktivirani", 20 | "Chapters": "Poglavlja", 21 | "You aborted the media playback": "Isključili ste reprodukciju videa.", 22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.", 25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/hr.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("hr",{ 2 | "Play": "Pusti", 3 | "Pause": "Pauza", 4 | "Current Time": "Trenutno vrijeme", 5 | "Duration Time": "Vrijeme trajanja", 6 | "Remaining Time": "Preostalo vrijeme", 7 | "Stream Type": "Način strimovanja", 8 | "LIVE": "UŽIVO", 9 | "Loaded": "Učitan", 10 | "Progress": "Progres", 11 | "Fullscreen": "Puni ekran", 12 | "Non-Fullscreen": "Mali ekran", 13 | "Mute": "Prigušen", 14 | "Unmuted": "Ne-prigušen", 15 | "Playback Rate": "Stopa reprodukcije", 16 | "Subtitles": "Podnaslov", 17 | "subtitles off": "Podnaslov deaktiviran", 18 | "Captions": "Titlovi", 19 | "captions off": "Titlovi deaktivirani", 20 | "Chapters": "Poglavlja", 21 | "You aborted the media playback": "Isključili ste reprodukciju videa.", 22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.", 25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/pt-BR.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("pt-BR",{ 2 | "Play": "Tocar", 3 | "Pause": "Pause", 4 | "Current Time": "Tempo", 5 | "Duration Time": "Duração", 6 | "Remaining Time": "Tempo Restante", 7 | "Stream Type": "Tipo de Stream", 8 | "LIVE": "AO VIVO", 9 | "Loaded": "Carregado", 10 | "Progress": "Progresso", 11 | "Fullscreen": "Tela Cheia", 12 | "Non-Fullscreen": "Tela Normal", 13 | "Mute": "Mudo", 14 | "Unmuted": "Habilitar Som", 15 | "Playback Rate": "Velocidade", 16 | "Subtitles": "Legendas", 17 | "subtitles off": "Sem Legendas", 18 | "Captions": "Anotações", 19 | "captions off": "Sem Anotações", 20 | "Chapters": "Capítulos", 21 | "You aborted the media playback": "Você parou a execução de vídeo.", 22 | "A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar parcialmente.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "O vídeo não pode ser carregado, ou porque houve um problema com sua rede ou pelo formato do vídeo não ser suportado.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A Execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.", 25 | "No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/sr.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("sr",{ 2 | "Play": "Pusti", 3 | "Pause": "Pauza", 4 | "Current Time": "Trenutno vrijeme", 5 | "Duration Time": "Vrijeme trajanja", 6 | "Remaining Time": "Preostalo vrijeme", 7 | "Stream Type": "Način strimovanja", 8 | "LIVE": "UŽIVO", 9 | "Loaded": "Učitan", 10 | "Progress": "Progres", 11 | "Fullscreen": "Puni ekran", 12 | "Non-Fullscreen": "Mali ekran", 13 | "Mute": "Prigušen", 14 | "Unmuted": "Ne-prigušen", 15 | "Playback Rate": "Stopa reprodukcije", 16 | "Subtitles": "Podnaslov", 17 | "subtitles off": "Podnaslov deaktiviran", 18 | "Captions": "Titlovi", 19 | "captions off": "Titlovi deaktivirani", 20 | "Chapters": "Poglavlja", 21 | "You aborted the media playback": "Isključili ste reprodukciju videa.", 22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.", 25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/DeleteCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SwiftClient.Cli 8 | { 9 | public static class DeleteCommand 10 | { 11 | public static int Run(DeleteOptions options, Client client) 12 | { 13 | if (string.IsNullOrEmpty(options.Object)) 14 | { 15 | var response = client.DeleteContainerWithContents(options.Container, options.Limit).Result; 16 | if (response.IsSuccess) 17 | { 18 | Console.WriteLine($"{options.Container} deleted"); 19 | } 20 | else 21 | { 22 | Console.WriteLine(response.Reason); 23 | return 404; 24 | } 25 | } 26 | else 27 | { 28 | var response = client.DeleteObject(options.Container, options.Object).Result; 29 | if (response.IsSuccess) 30 | { 31 | Console.WriteLine($"{options.Container}/{options.Object} deleted"); 32 | } 33 | else 34 | { 35 | Logger.LogError(response.Reason); 36 | return 404; 37 | } 38 | } 39 | 40 | 41 | return 0; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/fi.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("fi",{ 2 | "Play": "Toisto", 3 | "Pause": "Tauko", 4 | "Current Time": "Tämänhetkinen aika", 5 | "Duration Time": "Kokonaisaika", 6 | "Remaining Time": "Jäljellä oleva aika", 7 | "Stream Type": "Lähetystyyppi", 8 | "LIVE": "LIVE", 9 | "Loaded": "Ladattu", 10 | "Progress": "Edistyminen", 11 | "Fullscreen": "Koko näyttö", 12 | "Non-Fullscreen": "Koko näyttö pois", 13 | "Mute": "Ääni pois", 14 | "Unmuted": "Ääni päällä", 15 | "Playback Rate": "Toistonopeus", 16 | "Subtitles": "Tekstitys", 17 | "subtitles off": "Tekstitys pois", 18 | "Captions": "Tekstitys", 19 | "captions off": "Tekstitys pois", 20 | "Chapters": "Kappaleet", 21 | "You aborted the media playback": "Olet keskeyttänyt videotoiston.", 22 | "A network error caused the media download to fail part-way.": "Verkkovirhe keskeytti videon latauksen.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videon lataus ei onnistunut joko palvelin- tai verkkovirheestä tai väärästä formaatista johtuen.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videon toisto keskeytyi, koska media on vaurioitunut tai käyttää käyttää toimintoja, joita selaimesi ei tue.", 25 | "No compatible source was found for this media.": "Tälle videolle ei löytynyt yhteensopivaa lähdettä." 26 | }); -------------------------------------------------------------------------------- /test/SwiftClient.Test/SwiftLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Net; 4 | using Xunit.Abstractions; 5 | 6 | namespace SwiftClient.Test 7 | { 8 | public class SwiftLogger : ISwiftLogger 9 | { 10 | readonly ITestOutputHelper Output; 11 | 12 | private string _authError = "Exception occured: {0} for credentials {1} : {2} on proxy node {3}"; 13 | private string _requestError = "Exception occured: {0} with status code: {1} for request url: {2}"; 14 | private string _unauthorizedError = "Unauthorized request with old token {0}"; 15 | 16 | public SwiftLogger(ITestOutputHelper output) 17 | { 18 | Output = output; 19 | } 20 | 21 | public void LogAuthenticationError(Exception ex, string username, string password, string endpoint) 22 | { 23 | Output.WriteLine(string.Format(_authError, ex.InnerException != null ? ex.InnerException.Message : ex.Message, username, password, endpoint)); 24 | } 25 | 26 | public void LogRequestError(Exception ex, HttpStatusCode statusCode, string reason, string requestUrl) 27 | { 28 | Output.WriteLine(string.Format(_requestError, reason, statusCode.ToString(), requestUrl)); 29 | } 30 | 31 | public void LogUnauthorizedError(string token, string endpoint) 32 | { 33 | Output.WriteLine(string.Format(_unauthorizedError, token)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/hu.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("hu",{ 2 | "Play": "Lejátszás", 3 | "Pause": "Szünet", 4 | "Current Time": "Aktuális időpont", 5 | "Duration Time": "Hossz", 6 | "Remaining Time": "Hátralévő idő", 7 | "Stream Type": "Adatfolyam típusa", 8 | "LIVE": "ÉLŐ", 9 | "Loaded": "Betöltve", 10 | "Progress": "Állapot", 11 | "Fullscreen": "Teljes képernyő", 12 | "Non-Fullscreen": "Normál méret", 13 | "Mute": "Némítás", 14 | "Unmuted": "Némítás kikapcsolva", 15 | "Playback Rate": "Lejátszási sebesség", 16 | "Subtitles": "Feliratok", 17 | "subtitles off": "Feliratok kikapcsolva", 18 | "Captions": "Magyarázó szöveg", 19 | "captions off": "Magyarázó szöveg kikapcsolva", 20 | "Chapters": "Fejezetek", 21 | "You aborted the media playback": "Leállította a lejátszást", 22 | "A network error caused the media download to fail part-way.": "Hálózati hiba miatt a videó részlegesen töltődött le.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "A videó nem tölthető be hálózati vagy kiszolgálói hiba miatt, vagy a formátuma nem támogatott.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A lejátszás adatsérülés miatt leállt, vagy a videó egyes tulajdonságait a böngészője nem támogatja.", 25 | "No compatible source was found for this media.": "Nincs kompatibilis forrás ehhez a videóhoz." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/sv.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("sv",{ 2 | "Play": "Spela", 3 | "Pause": "Pausa", 4 | "Current Time": "Aktuell tid", 5 | "Duration Time": "Total tid", 6 | "Remaining Time": "Återstående tid", 7 | "Stream Type": "Strömningstyp", 8 | "LIVE": "LIVE", 9 | "Loaded": "Laddad", 10 | "Progress": "Förlopp", 11 | "Fullscreen": "Fullskärm", 12 | "Non-Fullscreen": "Ej fullskärm", 13 | "Mute": "Ljud av", 14 | "Unmuted": "Ljud på", 15 | "Playback Rate": "Uppspelningshastighet", 16 | "Subtitles": "Text på", 17 | "subtitles off": "Text av", 18 | "Captions": "Text på", 19 | "captions off": "Text av", 20 | "Chapters": "Kapitel", 21 | "You aborted the media playback": "Du har avbrutit videouppspelningen.", 22 | "A network error caused the media download to fail part-way.": "Ett nätverksfel gjorde att nedladdningen av videon avbröts.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Det gick inte att ladda videon, antingen på grund av ett server- eller nätverksfel, eller för att formatet inte stöds.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Uppspelningen avbröts på grund av att videon är skadad, eller också för att videon använder funktioner som din webbläsare inte stöder.", 25 | "No compatible source was found for this media.": "Det gick inte att hitta någon kompatibel källa för den här videon." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/uk.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("uk",{ 2 | "Play": "Відтворити", 3 | "Pause": "Призупинити", 4 | "Current Time": "Поточний час", 5 | "Duration Time": "Тривалість", 6 | "Remaining Time": "Час, що залишився", 7 | "Stream Type": "Тип потоку", 8 | "LIVE": "НАЖИВО", 9 | "Loaded": "Завантаження", 10 | "Progress": "Прогрес", 11 | "Fullscreen": "Повноекранний режим", 12 | "Non-Fullscreen": "Неповноекранний режим", 13 | "Mute": "Без звуку", 14 | "Unmuted": "Зі звуком", 15 | "Playback Rate": "Швидкість відтворення", 16 | "Subtitles": "Субтитри", 17 | "subtitles off": "Без субтитрів", 18 | "Captions": "Підписи", 19 | "captions off": "Без підписів", 20 | "Chapters": "Розділи", 21 | "You aborted the media playback": "Ви припинили відтворення відео", 22 | "A network error caused the media download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.", 25 | "No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/da.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("da",{ 2 | "Play": "Afspil", 3 | "Pause": "Pause", 4 | "Current Time": "Aktuel tid", 5 | "Duration Time": "Varighed", 6 | "Remaining Time": "Resterende tid", 7 | "Stream Type": "Stream-type", 8 | "LIVE": "LIVE", 9 | "Loaded": "Indlæst", 10 | "Progress": "Status", 11 | "Fullscreen": "Fuldskærm", 12 | "Non-Fullscreen": "Luk fuldskærm", 13 | "Mute": "Uden lyd", 14 | "Unmuted": "Med lyd", 15 | "Playback Rate": "Afspilningsrate", 16 | "Subtitles": "Undertekster", 17 | "subtitles off": "Uden undertekster", 18 | "Captions": "Undertekster for hørehæmmede", 19 | "captions off": "Uden undertekster for hørehæmmede", 20 | "Chapters": "Kapitler", 21 | "You aborted the media playback": "Du afbrød videoafspilningen.", 22 | "A network error caused the media download to fail part-way.": "En netværksfejl fik download af videoen til at fejle.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikke indlæses, enten fordi serveren eller netværket fejlede, eller fordi formatet ikke er understøttet.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoafspilningen blev afbrudt på grund af ødelagte data eller fordi videoen benyttede faciliteter som din browser ikke understøtter.", 25 | "No compatible source was found for this media.": "Fandt ikke en kompatibel kilde for denne media." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ru.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ru",{ 2 | "Play": "Воспроизвести", 3 | "Pause": "Приостановить", 4 | "Current Time": "Текущее время", 5 | "Duration Time": "Продолжительность", 6 | "Remaining Time": "Оставшееся время", 7 | "Stream Type": "Тип потока", 8 | "LIVE": "ОНЛАЙН", 9 | "Loaded": "Загрузка", 10 | "Progress": "Прогресс", 11 | "Fullscreen": "Полноэкранный режим", 12 | "Non-Fullscreen": "Неполноэкранный режим", 13 | "Mute": "Без звука", 14 | "Unmuted": "Со звуком", 15 | "Playback Rate": "Скорость воспроизведения", 16 | "Subtitles": "Субтитры", 17 | "subtitles off": "Субтитры выкл.", 18 | "Captions": "Подписи", 19 | "captions off": "Подписи выкл.", 20 | "Chapters": "Главы", 21 | "You aborted the media playback": "Вы прервали воспроизведение видео", 22 | "A network error caused the media download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.", 25 | "No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/bg.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("bg",{ 2 | "Play": "Възпроизвеждане", 3 | "Pause": "Пауза", 4 | "Current Time": "Текущо време", 5 | "Duration Time": "Продължителност", 6 | "Remaining Time": "Оставащо време", 7 | "Stream Type": "Тип на потока", 8 | "LIVE": "НА ЖИВО", 9 | "Loaded": "Заредено", 10 | "Progress": "Прогрес", 11 | "Fullscreen": "Цял екран", 12 | "Non-Fullscreen": "Спиране на цял екран", 13 | "Mute": "Без звук", 14 | "Unmuted": "Със звук", 15 | "Playback Rate": "Скорост на възпроизвеждане", 16 | "Subtitles": "Субтитри", 17 | "subtitles off": "Спряни субтитри", 18 | "Captions": "Аудио надписи", 19 | "captions off": "Спряни аудио надписи", 20 | "Chapters": "Глави", 21 | "You aborted the media playback": "Спряхте възпроизвеждането на видеото", 22 | "A network error caused the media download to fail part-way.": "Грешка в мрежата провали изтеглянето на видеото.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Видеото не може да бъде заредено заради проблем със сървъра или мрежата или защото този формат не е поддържан.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Възпроизвеждането на видеото беше прекъснато заради проблем с файла или защото видеото използва опции които браузърът Ви не поддържа.", 25 | "No compatible source was found for this media.": "Не беше намерен съвместим източник за това видео." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/nl.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("nl",{ 2 | "Play": "Afspelen", 3 | "Pause": "Pauze", 4 | "Current Time": "Huidige Tijd", 5 | "Duration Time": "Looptijd", 6 | "Remaining Time": "Resterende Tijd", 7 | "Stream Type": "Stream Type", 8 | "LIVE": "LIVE", 9 | "Loaded": "Geladen", 10 | "Progress": "Status", 11 | "Fullscreen": "Volledig scherm", 12 | "Non-Fullscreen": "Geen volledig scherm", 13 | "Mute": "Geluid Uit", 14 | "Unmuted": "Geluid Aan", 15 | "Playback Rate": "Weergave Rate", 16 | "Subtitles": "Ondertiteling", 17 | "subtitles off": "Ondertiteling uit", 18 | "Captions": "Onderschriften", 19 | "captions off": "Onderschriften uit", 20 | "Chapters": "Hoofdstukken", 21 | "You aborted the media playback": "Je hebt de media weergave afgebroken.", 22 | "A network error caused the media download to fail part-way.": "De media download is mislukt door een netwerkfout.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, veroorzaakt door een server of netwerkfout of het formaat word niet ondersteund.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De media weergave is afgebroken omdat deze beschadigd is of de media gebruikt functionaliteit die niet door je browser word ondersteund.", 25 | "No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/SwiftClient.Cli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwiftClient Command-line interface 5 | Stefan Prodan 6 | SwiftClient.Cli 7 | Exe 8 | SwiftClient.Cli 9 | false 10 | false 11 | false 12 | netstandard2.0 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | x64 32 | 33 | 34 | 35 | x64 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if hash unzip 2>/dev/null; then 4 | echo "unzip detected" 5 | else 6 | sudo apt-get install unzip curl 7 | fi 8 | 9 | if hash dnx 2>/dev/null; then 10 | echo "DNX detected, skipping prerequisites" 11 | else 12 | #install Mono 13 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF 14 | echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list 15 | sudo apt-get update 16 | sudo apt-get install mono-devel mono-complete 17 | 18 | # install DNVM 19 | curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh 20 | 21 | # install DNX for Mono 22 | dnvm upgrade -r mono 23 | fi 24 | 25 | # install Swift.Cli 26 | CLI_USER_HOME=~/swift-client-cli 27 | CLI_SOURCE="https://get.veritech.io/packages/SwiftClient.Cli/latest" 28 | 29 | mkdir -p $CLI_USER_HOME 30 | cd $CLI_USER_HOME 31 | 32 | if [ -s "$CLI_USER_HOME/SwiftClient.Cli" ]; then 33 | echo "SwiftClient.Cli is already installed in $CLI_USER_HOME, trying to update" 34 | else 35 | echo "Downloading SwiftClient.Cli package to '$CLI_USER_HOME'" 36 | fi 37 | 38 | curl -sS $CLI_SOURCE > package.zip || { 39 | echo >&2 "Failed to download '$CLI_SOURCE'.." 40 | return 1 41 | } 42 | 43 | unzip package.zip 44 | rm package.zip 45 | chmod +x $CLI_USER_HOME/SwiftClient.Cli 46 | 47 | echo "Type `$CLI_USER_HOME/SwiftClient.Cli -h -u -p ` to start SwiftClient.Cli" 48 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/it.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("it",{ 2 | "Play": "Play", 3 | "Pause": "Pausa", 4 | "Current Time": "Orario attuale", 5 | "Duration Time": "Durata", 6 | "Remaining Time": "Tempo rimanente", 7 | "Stream Type": "Tipo del Streaming", 8 | "LIVE": "LIVE", 9 | "Loaded": "Caricato", 10 | "Progress": "Stato", 11 | "Fullscreen": "Schermo intero", 12 | "Non-Fullscreen": "Chiudi schermo intero", 13 | "Mute": "Muto", 14 | "Unmuted": "Audio", 15 | "Playback Rate": "Tasso di riproduzione", 16 | "Subtitles": "Sottotitoli", 17 | "subtitles off": "Senza sottotitoli", 18 | "Captions": "Sottotitoli non udenti", 19 | "captions off": "Senza sottotitoli non udenti", 20 | "Chapters": "Capitolo", 21 | "You aborted the media playback": "La riproduzione del filmato è stata interrotta.", 22 | "A network error caused the media download to fail part-way.": "Il download del filmato è stato interrotto a causa di un problema rete.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Il filmato non può essere caricato a causa di un errore nel server o nella rete o perché il formato non viene supportato.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La riproduzione del filmato è stata interrotta a causa di un file danneggiato o per l’utilizzo di impostazioni non supportate dal browser.", 25 | "No compatible source was found for this media.": "Non ci sono fonti compatibili per questo filmato." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/ca.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ca",{ 2 | "Play": "Reproducció", 3 | "Pause": "Pausa", 4 | "Current Time": "Temps reproduït", 5 | "Duration Time": "Durada total", 6 | "Remaining Time": "Temps restant", 7 | "Stream Type": "Tipus de seqüència", 8 | "LIVE": "EN DIRECTE", 9 | "Loaded": "Carregat", 10 | "Progress": "Progrés", 11 | "Fullscreen": "Pantalla completa", 12 | "Non-Fullscreen": "Pantalla no completa", 13 | "Mute": "Silencia", 14 | "Unmuted": "Amb so", 15 | "Playback Rate": "Velocitat de reproducció", 16 | "Subtitles": "Subtítols", 17 | "subtitles off": "Subtítols desactivats", 18 | "Captions": "Llegendes", 19 | "captions off": "Llegendes desactivades", 20 | "Chapters": "Capítols", 21 | "You aborted the media playback": "Heu interromput la reproducció del vídeo.", 22 | "A network error caused the media download to fail part-way.": "Un error de la xarxa ha interromput la baixada del vídeo.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "No s'ha pogut carregar el vídeo perquè el servidor o la xarxa han fallat, o bé perquè el seu format no és compatible.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducció de vídeo s'ha interrumput per un problema de corrupció de dades o bé perquè el vídeo demanava funcions que el vostre navegador no ofereix.", 25 | "No compatible source was found for this media.": "No s'ha trobat cap font compatible amb el vídeo." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/de.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("de",{ 2 | "Play": "Wiedergabe", 3 | "Pause": "Pause", 4 | "Current Time": "Aktueller Zeitpunkt", 5 | "Duration Time": "Dauer", 6 | "Remaining Time": "Verbleibende Zeit", 7 | "Stream Type": "Streamtyp", 8 | "LIVE": "LIVE", 9 | "Loaded": "Geladen", 10 | "Progress": "Status", 11 | "Fullscreen": "Vollbild", 12 | "Non-Fullscreen": "Kein Vollbild", 13 | "Mute": "Ton aus", 14 | "Unmuted": "Ton ein", 15 | "Playback Rate": "Wiedergabegeschwindigkeit", 16 | "Subtitles": "Untertitel", 17 | "subtitles off": "Untertitel aus", 18 | "Captions": "Untertitel", 19 | "captions off": "Untertitel aus", 20 | "Chapters": "Kapitel", 21 | "You aborted the media playback": "Sie haben die Videowiedergabe abgebrochen.", 22 | "A network error caused the media download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.", 25 | "No compatible source was found for this media.": "Für dieses Video wurde keine kompatible Quelle gefunden." 26 | }); -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/fr.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("fr",{ 2 | "Play": "Lecture", 3 | "Pause": "Pause", 4 | "Current Time": "Temps actuel", 5 | "Duration Time": "Durée", 6 | "Remaining Time": "Temps restant", 7 | "Stream Type": "Type de flux", 8 | "LIVE": "EN DIRECT", 9 | "Loaded": "Chargé", 10 | "Progress": "Progression", 11 | "Fullscreen": "Plein écran", 12 | "Non-Fullscreen": "Fenêtré", 13 | "Mute": "Sourdine", 14 | "Unmuted": "Son activé", 15 | "Playback Rate": "Vitesse de lecture", 16 | "Subtitles": "Sous-titres", 17 | "subtitles off": "Sous-titres désactivés", 18 | "Captions": "Sous-titres", 19 | "captions off": "Sous-titres désactivés", 20 | "Chapters": "Chapitres", 21 | "You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.", 22 | "A network error caused the media download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.", 25 | "No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo." 26 | }); -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftServiceLogger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | 8 | namespace SwiftClient.AspNetCore 9 | { 10 | public class SwiftServiceLogger : ISwiftLogger 11 | { 12 | private readonly ILogger _logger; 13 | 14 | private readonly string _authError = "Exception occured: {0} for credentials {1} : {2} on proxy node {3}"; 15 | private readonly string _requestError = "Exception occured: {0} with status code: {1} for request url: {2}"; 16 | private readonly string _unauthorizedError = "Unauthorized request with old token {0} for request url: {1}"; 17 | 18 | public SwiftServiceLogger(ILogger logger) 19 | { 20 | _logger = logger; 21 | } 22 | 23 | public void LogAuthenticationError(Exception ex, string username, string password, string endpoint) 24 | { 25 | _logger.LogError(_authError, ex.InnerException != null ? ex.InnerException.Message : ex.Message, username, password, endpoint); 26 | } 27 | 28 | public void LogRequestError(Exception ex, HttpStatusCode statusCode, string reason, string requestUrl) 29 | { 30 | _logger.LogError(_requestError, reason, statusCode.ToString(), requestUrl); 31 | } 32 | 33 | public void LogUnauthorizedError(string token, string endpoint) 34 | { 35 | _logger.LogWarning(_unauthorizedError, token, endpoint); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/lang/es.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("es",{ 2 | "Play": "Reproducción", 3 | "Pause": "Pausa", 4 | "Current Time": "Tiempo reproducido", 5 | "Duration Time": "Duración total", 6 | "Remaining Time": "Tiempo restante", 7 | "Stream Type": "Tipo de secuencia", 8 | "LIVE": "DIRECTO", 9 | "Loaded": "Cargado", 10 | "Progress": "Progreso", 11 | "Fullscreen": "Pantalla completa", 12 | "Non-Fullscreen": "Pantalla no completa", 13 | "Mute": "Silenciar", 14 | "Unmuted": "No silenciado", 15 | "Playback Rate": "Velocidad de reproducción", 16 | "Subtitles": "Subtítulos", 17 | "subtitles off": "Subtítulos desactivados", 18 | "Captions": "Subtítulos especiales", 19 | "captions off": "Subtítulos especiales desactivados", 20 | "Chapters": "Capítulos", 21 | "You aborted the media playback": "Ha interrumpido la reproducción del vídeo.", 22 | "A network error caused the media download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.", 23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "No se ha podido cargar el vídeo debido a un fallo de red o del servidor o porque el formato es incompatible.", 24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.", 25 | "No compatible source was found for this media.": "No se ha encontrado ninguna fuente compatible con este vídeo." 26 | }); -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftClientServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SwiftClient; 3 | using SwiftClient.AspNetCore; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class SwiftClientServiceCollectionExtensions 8 | { 9 | 10 | public static IServiceCollection AddSwift( 11 | this IServiceCollection serviceCollection) 12 | { 13 | if (serviceCollection == null) 14 | { 15 | throw new ArgumentNullException(nameof(serviceCollection)); 16 | } 17 | 18 | serviceCollection.AddHttpClient("swift"); 19 | serviceCollection.AddOptions(); 20 | serviceCollection.AddSingleton(); 21 | serviceCollection.AddSingleton(); 22 | serviceCollection.AddTransient(); 23 | 24 | return serviceCollection; 25 | } 26 | 27 | public static IServiceCollection AddSwift( 28 | this IServiceCollection serviceCollection, 29 | Action configure) 30 | { 31 | if (serviceCollection == null) 32 | { 33 | throw new ArgumentNullException(nameof(serviceCollection)); 34 | } 35 | 36 | if (configure == null) 37 | { 38 | throw new ArgumentNullException(nameof(configure)); 39 | } 40 | 41 | serviceCollection.Configure(configure); 42 | return serviceCollection.AddSwift(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /tools/docker-swift/files/proxy-server.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | bind_port = 8080 3 | workers = 1 4 | user = swift 5 | log_facility = LOG_LOCAL1 6 | eventlet_debug = true 7 | 8 | [pipeline:main] 9 | # Yes, proxy-logging appears twice. This is so that 10 | # middleware-originated requests get logged too. 11 | pipeline = catch_errors gatekeeper healthcheck proxy-logging cache bulk tempurl slo dlo ratelimit crossdomain tempauth staticweb container-quotas account-quotas proxy-logging proxy-server 12 | 13 | [filter:catch_errors] 14 | use = egg:swift#catch_errors 15 | 16 | [filter:healthcheck] 17 | use = egg:swift#healthcheck 18 | 19 | [filter:proxy-logging] 20 | use = egg:swift#proxy_logging 21 | 22 | [filter:bulk] 23 | use = egg:swift#bulk 24 | 25 | [filter:ratelimit] 26 | use = egg:swift#ratelimit 27 | 28 | [filter:crossdomain] 29 | use = egg:swift#crossdomain 30 | 31 | [filter:dlo] 32 | use = egg:swift#dlo 33 | 34 | [filter:slo] 35 | use = egg:swift#slo 36 | 37 | [filter:tempurl] 38 | use = egg:swift#tempurl 39 | 40 | [filter:tempauth] 41 | storage_url_scheme = default 42 | use = egg:swift#tempauth 43 | user_admin_admin = admin .admin .reseller_admin 44 | user_test_tester = testing .admin 45 | user_test2_tester2 = testing2 .admin 46 | user_test_tester3 = testing3 47 | 48 | [filter:staticweb] 49 | use = egg:swift#staticweb 50 | 51 | [filter:account-quotas] 52 | use = egg:swift#account_quotas 53 | 54 | [filter:container-quotas] 55 | use = egg:swift#container_quotas 56 | 57 | [filter:cache] 58 | use = egg:swift#memcache 59 | 60 | [filter:gatekeeper] 61 | use = egg:swift#gatekeeper 62 | 63 | [app:proxy-server] 64 | use = egg:swift#proxy 65 | allow_account_management = true 66 | account_autocreate = true -------------------------------------------------------------------------------- /src/SwiftClient/Extensions/HttpExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | using System.Linq; 4 | using System.Net.Http.Headers; 5 | 6 | namespace SwiftClient.Extensions 7 | { 8 | internal static class HttpExtensions 9 | { 10 | public static void SetHeaders(this HttpRequestMessage request, Dictionary headers = null) 11 | { 12 | if (headers == null) return; 13 | foreach (var header in headers) 14 | { 15 | request.Headers.TryAddWithoutValidation(header.Key, header.Value); 16 | } 17 | } 18 | 19 | public static void SetHeaders(this HttpContent content, Dictionary headers) 20 | { 21 | if (headers == null) return; 22 | foreach (var header in headers) 23 | { 24 | content.Headers.TryAddWithoutValidation(header.Key, header.Value); 25 | } 26 | } 27 | 28 | public static string GetHeader(this HttpResponseMessage rsp, string headerName) 29 | { 30 | IEnumerable headers = new List(); 31 | return rsp.Headers.TryGetValues(headerName, out headers) ? headers.FirstOrDefault() : null; 32 | } 33 | 34 | public static Dictionary ToDictionary(this HttpResponseHeaders headers) 35 | { 36 | if (headers == null) return null; 37 | 38 | var result = new Dictionary(); 39 | 40 | foreach (var header in headers) 41 | { 42 | result.Add(header.Key, header.Value.FirstOrDefault()); 43 | } 44 | 45 | return result; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/SwiftClient/SwiftUrlBuilder.cs: -------------------------------------------------------------------------------- 1 | using SwiftClient.Extensions; 2 | using System.Collections.Generic; 3 | 4 | namespace SwiftClient 5 | { 6 | public static class SwiftUrlBuilder 7 | { 8 | public static string GetAccountUrl(string storageUrl, Dictionary queryParams = null) 9 | { 10 | var url = storageUrl; 11 | 12 | if (queryParams != null) 13 | { 14 | url += queryParams.ToQueryString(); 15 | } 16 | 17 | return url; 18 | } 19 | 20 | public static string GetContainerUrl(string storageUrl, string containerId, Dictionary queryParams = null) 21 | { 22 | var url = storageUrl + "/" + containerId.Encode(); 23 | 24 | if (queryParams != null) 25 | { 26 | url += queryParams.ToQueryString(); 27 | } 28 | 29 | return url; 30 | } 31 | 32 | public static string GetObjectUrl(string storageUrl, string containerId, string objectId, Dictionary queryParams = null) 33 | { 34 | var url = storageUrl + "/" + containerId.Encode() + "/" + objectId.Encode(); 35 | 36 | if (queryParams != null) 37 | { 38 | url += queryParams.ToQueryString(); 39 | } 40 | 41 | return url; 42 | } 43 | 44 | public static string GetAuthUrl(string rootUrl) 45 | { 46 | return rootUrl + "/auth/v1.0"; 47 | } 48 | 49 | public static string GetObjectChunkId(string objectId, int segment) 50 | { 51 | return string.Format("{0}/{1}", objectId, segment.ToString("0000")); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftClient.AspNetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenStack Swift client for ASP.NET Core MVC 5 | 2.1.9-beta 6 | Cristi Pufu;Stefan Prodan 7 | SwiftClient.AspNetCore 8 | SwiftClient.AspNetCore 9 | aspnetcore;OpenStack;Swift 10 | https://github.com/vtfuture/SwiftClient 11 | http://opensource.org/licenses/MIT 12 | git 13 | https://github.com/vtfuture/SwiftClient 14 | 2.0 15 | false 16 | false 17 | false 18 | true 19 | netstandard2.0 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | x64 32 | 33 | 34 | 35 | x64 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | 6 | /* Set widths on the form inputs since otherwise they're 100% wide */ 7 | input, 8 | select, 9 | textarea { 10 | max-width: 280px; 11 | } 12 | 13 | .alert { 14 | word-break: break-word; 15 | } 16 | 17 | 18 | h2 { 19 | font-size: 16px; 20 | text-transform: uppercase; 21 | margin: 30px 0 10px; 22 | } 23 | 24 | .upload-file { 25 | padding: 15px; 26 | margin-bottom: 20px; 27 | border: 1px dashed #BDBDBD; 28 | border-radius: 4px; 29 | } 30 | 31 | .list-group { 32 | margin: 11px 0 0; 33 | } 34 | 35 | .list-group .list-group-item > .glyphicon { 36 | margin-right: 5px; 37 | } 38 | 39 | .glyphicon-folder-open { 40 | background: #337ab7; 41 | color: #fff; 42 | padding: 10px; 43 | border-radius: 50%; 44 | } 45 | 46 | .glyphicon-file, .glyphicon-facetime-video { 47 | background: #31b0d5; 48 | color: #fff; 49 | padding: 10px; 50 | border-radius: 50%; 51 | } 52 | 53 | .list-group .list-group-item > .tree-toogle { 54 | color: #999; 55 | width: 55px; 56 | line-height: 55px; 57 | position: absolute; 58 | top: 0; 59 | right: 0; 60 | text-align: center; 61 | margin: 0; 62 | border-left: 1px solid #BDBDBD; 63 | } 64 | 65 | .list-group .list-group-item > .tree-toogle:hover { 66 | color: #666; 67 | cursor: pointer; 68 | } 69 | 70 | .error_page { 71 | background: #337ab7; 72 | color: #fff; 73 | padding: 20px; 74 | border-radius: 3px; 75 | } 76 | .error_page h1 { 77 | margin: 0 0 30px; 78 | padding-bottom: 10px; 79 | border-bottom: 1px dashed #6A9FCC; 80 | } 81 | .error_page p { 82 | font-size: 17px; 83 | } 84 | 85 | @media (max-width: 991px) { 86 | .btn-group { 87 | margin-top: 15px; 88 | width: 100%; 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/SwiftClient/SwiftHeaderKeys.cs: -------------------------------------------------------------------------------- 1 | using SwiftClient.Extensions; 2 | 3 | namespace SwiftClient 4 | { 5 | public static class SwiftHeaderKeys 6 | { 7 | public static string AuthUser = "X-Auth-User"; 8 | public static string AuthKey = "X-Auth-Key"; 9 | public static string AuthToken = "X-Auth-Token"; 10 | public static string StorageUrl = "X-Storage-Url"; 11 | public static string ObjectManifest = "X-Object-Manifest"; 12 | public static string ObjectManifestValueFormat = "{0}/{1}"; 13 | public static string CopyFrom = "X-Copy-From"; 14 | 15 | public static string AccountObjectCount = "X-Account-Object-Count"; 16 | public static string AccountBytesUsed = "X-Account-Bytes-Used"; 17 | public static string AccountContainerCount = "X-Account-Container-Count"; 18 | public static string AccountMetaFormat = "X-Account-Meta-{0}"; 19 | public static string AccountMeta = "X-Account-Meta"; 20 | 21 | public static string ContainerObjectCount = "X-Container-Object-Count"; 22 | public static string ContainerBytesUsed = "X-Container-Bytes-Used"; 23 | public static string ContainerMetaFormat = "X-Container-Meta-{0}"; 24 | public static string ContainerMeta = "X-Container-Meta"; 25 | 26 | public static string ObjectMetaFormat = "X-Object-Meta-{0}"; 27 | public static string ObjectMeta = "X-Object-Meta"; 28 | 29 | public static string ContentType = "Content-Type"; 30 | public static string ContentLength = "Content-Length"; 31 | public static string Range = "Range"; 32 | public static string RangeValueFormat = "bytes={0}-{1}"; 33 | 34 | public static string GetObjectManifestValue(string containerId, string objectId) 35 | { 36 | return string.Format(ObjectManifestValueFormat, containerId.Encode(), objectId.Encode()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/lib/video-js/dist/examples/simple-embed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Video.js | HTML5 Video Player 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftAuthManagerMemoryCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Linq; 5 | using Microsoft.Extensions.Caching.Memory; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace SwiftClient.AspNetCore 9 | { 10 | public class SwiftAuthManagerMemoryCache : ISwiftAuthManager 11 | { 12 | private readonly IMemoryCache cache; 13 | private readonly string authCacheKey = "swift_authdata"; 14 | private readonly string endpointsKey = "swift_endpoints"; 15 | 16 | public SwiftAuthManagerMemoryCache(IOptions options, IMemoryCache cache) 17 | { 18 | var _options = options.Value; 19 | Credentials = new SwiftCredentials { 20 | Endpoints = _options.Endpoints, 21 | Password = _options.Password, 22 | Username = _options.Username 23 | }; 24 | this.cache = cache; 25 | } 26 | 27 | public Func> Authenticate { get; set; } 28 | 29 | public SwiftCredentials Credentials { get; set; } 30 | 31 | public SwiftAuthData GetAuthData() 32 | { 33 | return cache.Get(authCacheKey); 34 | } 35 | 36 | public void SetAuthData(SwiftAuthData authData) 37 | { 38 | if (authData != null) 39 | { 40 | cache.Set(authCacheKey, authData); 41 | } 42 | } 43 | 44 | public List GetEndpoints() 45 | { 46 | return cache.Get>(endpointsKey) ?? Credentials.Endpoints; 47 | } 48 | 49 | public void SetEndpoints(List endpoints) 50 | { 51 | if (endpoints != null && endpoints.Any()) 52 | { 53 | cache.Set(endpointsKey, endpoints); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/SwiftClient/SwiftClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenStack Swift client for .NET 4.5 and .NET Core 5 | 2.1.5-beta 6 | Cristi Pufu;Stefan Prodan 7 | SwiftClient 8 | SwiftClient 9 | OpenStack;Swift 10 | https://github.com/vtfuture/SwiftClient 11 | http://opensource.org/licenses/MIT 12 | git 13 | https://github.com/vtfuture/SwiftClient 14 | 2.0 15 | false 16 | false 17 | false 18 | true 19 | netstandard2.0 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | x64 36 | 37 | 38 | 39 | x64 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/SwiftClient.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwiftClient.Test 5 | SwiftClient.Test 6 | true 7 | $(PackageTargetFallback);dotnet5.6;portable-net461+win8 8 | 1.0.4 9 | false 10 | false 11 | false 12 | netstandard2.0;netcoreapp2.1 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /tools/docker-swift/files/object-expirer.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | # swift_dir = /etc/swift 3 | user = swift 4 | # You can specify default log routing here if you want: 5 | log_name = object-expirer 6 | log_facility = LOG_LOCAL6 7 | log_level = INFO 8 | #log_address = /dev/log 9 | # 10 | # comma separated list of functions to call to setup custom log handlers. 11 | # functions get passed: conf, name, log_to_console, log_route, fmt, logger, 12 | # adapted_logger 13 | # log_custom_handlers = 14 | # 15 | # If set, log_udp_host will override log_address 16 | # log_udp_host = 17 | # log_udp_port = 514 18 | # 19 | # You can enable StatsD logging here: 20 | # log_statsd_host = localhost 21 | # log_statsd_port = 8125 22 | # log_statsd_default_sample_rate = 1.0 23 | # log_statsd_sample_rate_factor = 1.0 24 | # log_statsd_metric_prefix = 25 | 26 | [object-expirer] 27 | interval = 300 28 | # auto_create_account_prefix = . 29 | # report_interval = 300 30 | # concurrency is the level of concurrency o use to do the work, this value 31 | # must be set to at least 1 32 | # concurrency = 1 33 | # processes is how many parts to divide the work into, one part per process 34 | # that will be doing the work 35 | # processes set 0 means that a single process will be doing all the work 36 | # processes can also be specified on the command line and will override the 37 | # config value 38 | # processes = 0 39 | # process is which of the parts a particular process will work on 40 | # process can also be specified on the command line and will overide the config 41 | # value 42 | # process is "zero based", if you want to use 3 processes, you should run 43 | # processes with process set to 0, 1, and 2 44 | # process = 0 45 | 46 | [pipeline:main] 47 | pipeline = catch_errors cache proxy-server 48 | 49 | [app:proxy-server] 50 | use = egg:swift#proxy 51 | # See proxy-server.conf-sample for options 52 | 53 | [filter:cache] 54 | use = egg:swift#memcache 55 | # See proxy-server.conf-sample for options 56 | 57 | [filter:catch_errors] 58 | use = egg:swift#catch_errors 59 | # See proxy-server.conf-sample for options -------------------------------------------------------------------------------- /src/SwiftClient/SwiftClientConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SwiftClient 4 | { 5 | public partial class Client : ISwiftClient, IDisposable 6 | { 7 | 8 | /// 9 | /// Set credentials (username, password, list of proxy endpoints) 10 | /// 11 | /// 12 | /// 13 | public Client WithCredentials(SwiftCredentials credentials) 14 | { 15 | if (RetryManager == null) 16 | { 17 | var authManager = new SwiftAuthManager(credentials); 18 | 19 | authManager.Authenticate = Authenticate; 20 | 21 | authManager.Credentials = credentials; 22 | 23 | RetryManager = new SwiftRetryManager(authManager); 24 | } 25 | 26 | return this; 27 | } 28 | 29 | /// 30 | /// Log authentication errors, reauthorization events and request errors 31 | /// 32 | /// 33 | /// 34 | public Client SetLogger(ISwiftLogger logger) 35 | { 36 | _logger = logger; 37 | RetryManager.SetLogger(logger); 38 | 39 | return this; 40 | } 41 | 42 | /// 43 | /// Set retries count for all proxy nodes 44 | /// 45 | /// Default value 1 46 | /// 47 | public Client SetRetryCount(int retryCount) 48 | { 49 | RetryManager.SetRetryCount(retryCount); 50 | 51 | return this; 52 | } 53 | 54 | /// 55 | /// Set retries count per proxy node request 56 | /// 57 | /// Default value 1 58 | /// 59 | public Client SetRetryPerEndpointCount(int retryPerEndpointCount) 60 | { 61 | RetryManager.SetRetryPerEndpointCount(retryPerEndpointCount); 62 | 63 | return this; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CommandLine; 6 | using System.Text.RegularExpressions; 7 | using System.IO; 8 | using Newtonsoft.Json; 9 | using Humanizer; 10 | using Humanizer.Bytes; 11 | 12 | namespace SwiftClient.Cli 13 | { 14 | public class Program 15 | { 16 | private static Client client = null; 17 | 18 | public static void Main(string[] args) 19 | { 20 | var authManager = new AuthManager(args); 21 | 22 | client = authManager.Connect().Result; 23 | 24 | var command = Console.ReadLine(); 25 | 26 | while (command != "exit") 27 | { 28 | try 29 | { 30 | var exitCode = Parser.Default.ParseArguments< 31 | StatsOptions, 32 | PutOptions, 33 | GetOptions, 34 | ListOptions, 35 | ExportOptions, 36 | ImportOptions, 37 | DeleteOptions>(command.ParseArguments()).MapResult( 38 | (StatsOptions opts) => StatsCommand.Run(opts, client), 39 | (PutOptions opts) => PutCommand.Run(opts, client), 40 | (GetOptions opts) => GetCommand.Run(opts, client), 41 | (ListOptions opts) => ListCommand.Run(opts, client), 42 | (ExportOptions opts) => ExportCommand.Run(opts, client, authManager), 43 | (ImportOptions opts) => ImportCommand.Run(opts, client, authManager), 44 | (DeleteOptions opts) => DeleteCommand.Run(opts, client), 45 | errs => 1); 46 | } 47 | catch (Exception ex) 48 | { 49 | Logger.LogError(ex.InnerException != null ? ex.InnerException.Message : ex.Message); 50 | } 51 | 52 | command = Console.ReadLine(); 53 | } 54 | 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with ASP.NET Core and OpenStack Swift 2 | 3 | With ASP.NET Core being released .NET developers will switch to containers as the default deployment model, breaking away from IIS and monolithic apps is about to happen. 4 | Running ASP.NET web apps in containers being docker or windows containers will simplify and speed up not only deployment but also the build and test operations. 5 | 6 | Lets assume you have an ASP.NET Core MVC app that manages documents, photos and video files uploaded by users. 7 | Your application scales horizontally by running in multiple containers behind a reverse proxy. 8 | All your application instances must access the storage where user files are stored in order to function, but having the same storage shared between containers can a be a challenge. 9 | If you have a HA setup then your containers are running on multiple machines, probability not event in the same data center, so instead of storing the files on disk your application could use a distributed storage system like OpenStack Swift. 10 | 11 | OpenStack Swift is an eventually consistent storage system designed to scale horizontally without any single point of failure. All objects are stored with multiple copies and are replicated across zones and regions making Swift withstand failures in storage and network hardware. Swift can be used as a stand-alone distributed storage system on top of Linux without the need of expensive hardware solutions like NAS or SAN. 12 | Data is stored and served directly over HTTP making Swift the ideal solution when dealing with applications running in containers. 13 | 14 | Lets assume you've installed an OpenStack Swift cluster with a minimum of two swift servers, each containing a proxy and a storage node. Ideally these servers or VMs should be hosted in different regions/datacenters. 15 | In order to access the Swift cluster from your ASP.NET Core application you can use SwiftClient.AspNetCore package. Adding both swift proxy endpoints to SwfitClient config will ensure that any app instance will share the same storage and if a Swift node becomes unreachable due to a restart or network failure all app instances will silently fail-over to the 2nd node. 16 | 17 | -------------------------------------------------------------------------------- /test/SwiftClient.Test/SwiftFixture.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace SwiftClient.Test 9 | { 10 | public class SwiftFixture 11 | { 12 | public SwiftCredentials Credentials; 13 | public string ContainerId = "testcontainer"; 14 | public string PseudoDirectoryId = "pseudodirectory"; 15 | public string ObjectId = "testfile"; 16 | public string ChunkedObjectId = "testfilechunks"; 17 | public int MaxBufferSize = 10 * 1024; 18 | public int Chunks = 10; 19 | 20 | public SwiftFixture() 21 | { 22 | Credentials = GetConfigCredentials(); 23 | } 24 | 25 | public virtual void Dispose() 26 | { 27 | using (var client = new Client(Credentials)) 28 | { 29 | var deleteFilesTasks = new List(); 30 | 31 | deleteFilesTasks.Add(client.DeleteObject(ContainerId, ObjectId)); 32 | deleteFilesTasks.Add(client.DeleteObject(ContainerId, ChunkedObjectId)); 33 | 34 | for (var i = 0; i < Chunks; i++) 35 | { 36 | deleteFilesTasks.Add(client.DeleteObjectChunk(ContainerId, ChunkedObjectId, i)); 37 | } 38 | 39 | var delContainerTask = client.DeleteContainer(ContainerId); 40 | 41 | Task.WhenAll(deleteFilesTasks) 42 | .ContinueWith((rsp) => delContainerTask) 43 | .Wait(); 44 | } 45 | } 46 | 47 | SwiftCredentials GetConfigCredentials() 48 | { 49 | var services = new ServiceCollection(); 50 | var builder = new ConfigurationBuilder() 51 | .AddJsonFile("appsettings.json"); 52 | 53 | IConfigurationRoot configuration = builder.Build(); 54 | 55 | var section = configuration.GetSection("SwiftCluster"); 56 | 57 | return CustomConfigReader.Get(section); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tools/docker-swift/files/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=false 3 | 4 | [program:memcached] 5 | command=/usr/bin/memcached -u memcache 6 | startsecs=3 7 | stopwaitsecs = 3 8 | 9 | [program:rsyslog] 10 | command=/bin/bash -c "source /etc/default/rsyslog && /usr/sbin/rsyslogd -n -c3" 11 | startsecs = 5 12 | stopwaitsecs = 5 13 | 14 | [program:proxy-server] 15 | command=/usr/bin/python /usr/bin/swift-proxy-server /etc/swift/proxy-server.conf 16 | startsecs=3 17 | stopwaitsecs = 3 18 | 19 | [program:container-server] 20 | command=/usr/bin/python /usr/bin/swift-container-server /etc/swift/container-server.conf 21 | startsecs=3 22 | stopwaitsecs = 3 23 | 24 | [program:account-server] 25 | command=/usr/bin/python /usr/bin/swift-account-server /etc/swift/account-server.conf 26 | startsecs=3 27 | stopwaitsecs = 3 28 | 29 | [program:account-auditor] 30 | command=/usr/bin/python /usr/bin/swift-account-auditor /etc/swift/account-server.conf 31 | startsecs=3 32 | stopwaitsecs = 3 33 | 34 | [program:object-replicator] 35 | command=/usr/bin/python /usr/bin/swift-object-replicator /etc/swift/object-server.conf 36 | startsecs=3 37 | stopwaitsecs = 3 38 | 39 | [program:object-auditor] 40 | command=/usr/bin/python /usr/bin/swift-object-auditor /etc/swift/object-server.conf 41 | startsecs=3 42 | stopwaitsecs = 3 43 | 44 | [progam:container-auditor] 45 | command=/usr/bin/python /usr/bin/swift-container-auditor /etc/swift/container-server.conf 46 | startsecs=3 47 | stopwaitsecs = 3 48 | 49 | [program:object-auditor] 50 | command=/usr/bin/python /usr/bin/swift-object-server /etc/swift/object-server.conf 51 | startsecs=3 52 | stopwaitsecs = 3 53 | 54 | [program:account-replicator] 55 | command=/usr/bin/python /usr/bin/swift-account-replicator /etc/swift/account-server.conf 56 | startsecs=3 57 | stopwaitsecs = 3 58 | 59 | [program:account-reaper] 60 | command=/usr/bin/python /usr/bin/swift-account-reaper /etc/swift/account-server.conf 61 | startsecs=3 62 | stopwaitsecs = 3 63 | 64 | [program:container-sync] 65 | command=/usr/bin/python /usr/bin/swift-container-sync /etc/swift/container-server.conf 66 | startsecs=3 67 | stopwaitsecs = 3 68 | 69 | [program:object-updater] 70 | command=/usr/bin/python /usr/bin/swift-object-updater /etc/swift/object-server.conf 71 | startsecs=3 72 | stopwaitsecs = 3 73 | 74 | -------------------------------------------------------------------------------- /src/SwiftClient/SwiftClientAuthorization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using SwiftClient.Extensions; 6 | 7 | namespace SwiftClient 8 | { 9 | public partial class Client : ISwiftClient, IDisposable 10 | { 11 | /// 12 | /// Get authentication token and storage url 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | public Task Authenticate() 19 | { 20 | return RetryManager.Authenticate(); 21 | } 22 | 23 | private async Task Authenticate(string username, string password, string endpoint) 24 | { 25 | var url = SwiftUrlBuilder.GetAuthUrl(endpoint); 26 | 27 | var request = new HttpRequestMessage(HttpMethod.Get, url); 28 | 29 | FillRequest(request, null, new Dictionary 30 | { 31 | { SwiftHeaderKeys.AuthUser, username }, 32 | { SwiftHeaderKeys.AuthKey, password } 33 | }); 34 | 35 | try 36 | { 37 | using (var response = await _client.SendAsync(request).ConfigureAwait(false)) 38 | { 39 | response.EnsureSuccessStatusCode(); 40 | 41 | return new SwiftAuthData 42 | { 43 | AuthToken = response.GetHeader(SwiftHeaderKeys.AuthToken), 44 | StorageUrl = response.GetHeader(SwiftHeaderKeys.StorageUrl) 45 | }; 46 | } 47 | } 48 | catch (Exception ex) 49 | { 50 | if (_logger != null) 51 | { 52 | _logger.LogAuthenticationError(ex, username, password, endpoint); 53 | } 54 | 55 | return null; 56 | } 57 | } 58 | 59 | private Task AuthorizeAndExecute(Func> func) where T : SwiftBaseResponse, new() 60 | { 61 | return RetryManager.AuthorizeAndExecute(func); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/SwiftAuthManagerDistributedCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Linq; 5 | using Microsoft.Extensions.Caching.Memory; 6 | using Microsoft.Extensions.Caching.Distributed; 7 | using Newtonsoft.Json; 8 | using Microsoft.Extensions.Options; 9 | 10 | namespace SwiftClient.AspNetCore 11 | { 12 | public class SwiftAuthManagerDistributedCache : ISwiftAuthManager 13 | { 14 | private readonly IDistributedCache _cache; 15 | private readonly string authCacheKey = "swift_authdata"; 16 | private readonly string endpointsKey = "swift_endpoints"; 17 | 18 | public SwiftAuthManagerDistributedCache(IOptions options, IDistributedCache cache) 19 | { 20 | var _options = options.Value; 21 | Credentials = new SwiftCredentials { 22 | Endpoints = _options.Endpoints, 23 | Password = _options.Password, 24 | Username = _options.Username 25 | }; 26 | 27 | _cache = cache; 28 | } 29 | 30 | public Func> Authenticate { get; set; } 31 | 32 | public SwiftCredentials Credentials { get; set; } 33 | 34 | public SwiftAuthData GetAuthData() 35 | { 36 | var stored = _cache.GetString(authCacheKey); 37 | 38 | if (!string.IsNullOrEmpty(stored)) 39 | { 40 | return JsonConvert.DeserializeObject(stored); 41 | } 42 | 43 | return null; 44 | } 45 | 46 | public void SetAuthData(SwiftAuthData authData) 47 | { 48 | if (authData != null) 49 | { 50 | _cache.SetString(authCacheKey, JsonConvert.SerializeObject(authData)); 51 | } 52 | } 53 | 54 | public List GetEndpoints() 55 | { 56 | var stored = _cache.GetString(endpointsKey); 57 | 58 | if (!string.IsNullOrEmpty(stored)) 59 | { 60 | return JsonConvert.DeserializeObject>(stored); 61 | } 62 | 63 | return Credentials.Endpoints; 64 | } 65 | 66 | public void SetEndpoints(List endpoints) 67 | { 68 | if (endpoints != null && endpoints.Any()) 69 | { 70 | _cache.SetString(endpointsKey, JsonConvert.SerializeObject(endpoints)); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | # Taken from https://github.com/jbogard/MediatR 2 | 3 | <# 4 | .SYNOPSIS 5 | You can add this to you build script to ensure that psbuild is available before calling 6 | Invoke-MSBuild. If psbuild is not available locally it will be downloaded automatically. 7 | #> 8 | function EnsurePsbuildInstalled{ 9 | [cmdletbinding()] 10 | param( 11 | [string]$psbuildInstallUri = 'https://raw.githubusercontent.com/ligershark/psbuild/master/src/GetPSBuild.ps1' 12 | ) 13 | process{ 14 | if(-not (Get-Command "Invoke-MsBuild" -errorAction SilentlyContinue)){ 15 | 'Installing psbuild from [{0}]' -f $psbuildInstallUri | Write-Verbose 16 | (new-object Net.WebClient).DownloadString($psbuildInstallUri) | iex 17 | } 18 | else{ 19 | 'psbuild already loaded, skipping download' | Write-Verbose 20 | } 21 | 22 | # make sure it's loaded and throw if not 23 | if(-not (Get-Command "Invoke-MsBuild" -errorAction SilentlyContinue)){ 24 | throw ('Unable to install/load psbuild from [{0}]' -f $psbuildInstallUri) 25 | } 26 | } 27 | } 28 | 29 | # Taken from psake https://github.com/psake/psake 30 | 31 | <# 32 | .SYNOPSIS 33 | This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode 34 | to see if an error occcured. If an error is detected then an exception is thrown. 35 | This function allows you to run command-line programs without having to 36 | explicitly check the $lastexitcode variable. 37 | .EXAMPLE 38 | exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed" 39 | #> 40 | function Exec 41 | { 42 | [CmdletBinding()] 43 | param( 44 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, 45 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd) 46 | ) 47 | & $cmd 48 | if ($lastexitcode -ne 0) { 49 | throw ("Exec: " + $errorMessage) 50 | } 51 | } 52 | 53 | if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse } 54 | 55 | EnsurePsbuildInstalled 56 | 57 | exec { & dotnet restore } 58 | 59 | Invoke-MSBuild 60 | 61 | $revision = @{ $true = $env:APPVEYOR_BUILD_NUMBER; $false = 1 }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; 62 | $revision = "{0:D4}" -f [convert]::ToInt32($revision, 10) 63 | 64 | exec { & dotnet pack .\src\SwiftClient -c Release -o .\artifacts --version-suffix=$revision } 65 | exec { & dotnet pack .\src\SwiftClient.AspNetCore -c Release -o .\artifacts --version-suffix=$revision } 66 | 67 | 68 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SwiftClient.AspNetCore.Demo 12 | { 13 | public class Startup 14 | { 15 | public Startup(IHostingEnvironment env) 16 | { 17 | var builder = new ConfigurationBuilder() 18 | .SetBasePath(env.ContentRootPath) 19 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 20 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 21 | .AddEnvironmentVariables(); 22 | Configuration = builder.Build(); 23 | } 24 | 25 | public IConfigurationRoot Configuration { get; } 26 | 27 | // This method gets called by the runtime. Use this method to add services to the container. 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddOptions(); 31 | services.AddMemoryCache(); 32 | 33 | // Add framework services. 34 | services.AddMvc(); 35 | 36 | services.Configure(Configuration.GetSection("SwiftCluster")); 37 | services.AddSwift(); 38 | } 39 | 40 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 41 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 42 | { 43 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 44 | loggerFactory.AddDebug(); 45 | 46 | if (env.IsDevelopment()) 47 | { 48 | app.UseDeveloperExceptionPage(); 49 | app.UseBrowserLink(); 50 | } 51 | else 52 | { 53 | app.UseStatusCodePagesWithReExecute("/Error/Http{0}"); 54 | // Add Error handling middleware which catches all application specific errors and 55 | // send the request to the following path or controller action. 56 | app.UseExceptionHandler("/Error/Http500"); 57 | } 58 | 59 | app.UseStaticFiles(); 60 | 61 | app.UseMvc(routes => 62 | { 63 | routes.MapRoute( 64 | name: "default", 65 | template: "{controller=Home}/{action=Index}/{id?}"); 66 | }); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/SwiftClient.AspNetCore.Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | SwiftClient.AspNetCore.Demo 6 | Exe 7 | SwiftClient.AspNetCore.Demo 8 | 1.0.4 9 | $(PackageTargetFallback);dotnet5.6;portable-net45+win8 10 | netstandard2.0;netcoreapp2.1 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | x64 51 | 52 | 53 | 54 | x64 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/SwiftClient.AspNetCore/HttpExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Net.Http.Headers; 3 | using System; 4 | 5 | namespace SwiftClient.AspNetCore 6 | { 7 | public static class HttpExtensions 8 | { 9 | public static RangeHeaderValue GetRanges(this HttpContext context, long contentSize) 10 | { 11 | RangeHeaderValue rangesResult = null; 12 | 13 | string rangeHeader = context.Request.Headers["Range"]; 14 | 15 | if (!string.IsNullOrEmpty(rangeHeader)) 16 | { 17 | // rangeHeader contains the value of the Range HTTP Header and can have values like: 18 | // Range: bytes=0-1 * Get bytes 0 and 1, inclusive 19 | // Range: bytes=0-500 * Get bytes 0 to 500 (the first 501 bytes), inclusive 20 | // Range: bytes=400-1000 * Get bytes 500 to 1000 (501 bytes in total), inclusive 21 | // Range: bytes=-200 * Get the last 200 bytes 22 | // Range: bytes=500- * Get all bytes from byte 500 to the end 23 | // 24 | // Can also have multiple ranges delimited by commas, as in: 25 | // Range: bytes=0-500,600-1000 * Get bytes 0-500 (the first 501 bytes), inclusive plus bytes 600-1000 (401 bytes) inclusive 26 | 27 | // Remove "Ranges" and break up the ranges 28 | string[] ranges = rangeHeader.Replace("bytes=", string.Empty).Split(",".ToCharArray()); 29 | 30 | rangesResult = new RangeHeaderValue(); 31 | 32 | for (int i = 0; i < ranges.Length; i++) 33 | { 34 | const int START = 0, END = 1; 35 | 36 | long endByte, startByte; 37 | 38 | long parsedValue; 39 | 40 | string[] currentRange = ranges[i].Split("-".ToCharArray()); 41 | 42 | if (long.TryParse(currentRange[END], out parsedValue)) 43 | endByte = parsedValue; 44 | else 45 | endByte = contentSize - 1; 46 | 47 | 48 | if (long.TryParse(currentRange[START], out parsedValue)) 49 | startByte = parsedValue; 50 | else 51 | { 52 | // No beginning specified, get last n bytes of file 53 | // We already parsed end, so subtract from total and 54 | // make end the actual size of the file 55 | startByte = contentSize - endByte; 56 | endByte = contentSize - 1; 57 | } 58 | 59 | rangesResult.Ranges.Add(new RangeItemHeaderValue(startByte, endByte)); 60 | } 61 | } 62 | 63 | return rangesResult; 64 | } 65 | 66 | public static string GetFileName(this IFormFile file) 67 | { 68 | string contentDisposition = file.ContentDisposition; 69 | string filename = "filename="; 70 | int index = contentDisposition.LastIndexOf(filename, StringComparison.OrdinalIgnoreCase); 71 | if (index > -1) 72 | { 73 | return contentDisposition.Substring(index + filename.Length).Replace(" ", "-").Replace("\"", ""); 74 | } 75 | 76 | return null; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/SwiftClient/Interfaces/ISwiftClient.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace SwiftClient 7 | { 8 | public interface ISwiftClient 9 | { 10 | void SetHttpClient(IHttpClientFactory httpClientFactory = null, bool noDispose = true); 11 | 12 | Task Authenticate(); 13 | Task CopyObject(string containerFromId, string objectFromId, string containerToId, string objectToId, Dictionary headers = null); 14 | Task DeleteContainer(string containerId, Dictionary headers = null); 15 | Task DeleteObject(string containerId, string objectId, Dictionary queryParams = null); 16 | Task DeleteObjectChunk(string containerId, string objectId, int segment); 17 | Task DeleteObjects(IEnumerable objectIds); 18 | Task DeleteObjects(string containerId, IEnumerable objectIds); 19 | 20 | Task GetAccount(Dictionary queryParams = null); 21 | Task GetContainer(string containerId, Dictionary headers = null, Dictionary queryParams = null); 22 | Task GetObject(string containerId, string objectId, Dictionary headers = null, Dictionary queryParams = null); 23 | Task GetObjectRange(string containerId, string objectId, long start, long end, Dictionary headers = null, Dictionary queryParams = null); 24 | Task HeadAccount(); 25 | Task HeadContainer(string containerId, Dictionary headers = null); 26 | Task HeadObject(string containerId, string objectId, Dictionary headers = null, Dictionary queryParams = null); 27 | Task PostAccount(Dictionary headers = null); 28 | Task PostContainer(string containerId, Dictionary headers = null); 29 | Task PostObject(string containerId, string objectId, Dictionary headers = null); 30 | Task PutContainer(string containerId, Dictionary headers = null); 31 | Task PutManifest(string containerId, string objectId, Dictionary headers = null, Dictionary queryParams = null); 32 | Task PutObject(string containerId, string objectId, Stream data, Dictionary headers = null, Dictionary queryParams = null); 33 | Task PutObject(string containerId, string objectId, byte[] data, Dictionary headers = null, Dictionary queryParams = null); 34 | Task PutObjectChunk(string containerId, string objectId, byte[] data, int segment, Dictionary headers = null, Dictionary queryParams = null); 35 | Task PutPseudoDirectory(string containerId, string objectId, Dictionary headers = null, Dictionary queryParams = null); 36 | SwiftCredentials GetCredentials(); 37 | Client SetLogger(ISwiftLogger logger); 38 | Client SetRetryCount(int retryCount); 39 | Client SetRetryPerEndpointCount(int retryPerEndpointCount); 40 | Client WithCredentials(SwiftCredentials credentials); 41 | } 42 | } -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model PageViewModel 2 | @using Newtonsoft.Json 3 | 4 | @{ 5 | ViewData["Title"] = "Swift Client Demo"; 6 | } 7 | 8 | 9 | @if (!string.IsNullOrEmpty(Model.Message)) 10 | { 11 |
@Model.Message
12 | } 13 | 14 |
15 | 16 | 17 | 25 |
26 | 27 |

Your files

28 | 29 |
30 | 31 | 74 | 75 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Helpers/ProgressBar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | 5 | namespace SwiftClient.Cli 6 | { 7 | 8 | /// 9 | /// An ASCII progress bar 10 | /// 11 | public class ProgressBar : IDisposable, IProgress 12 | { 13 | private const int blockCount = 10; 14 | private readonly TimeSpan animationInterval = TimeSpan.FromSeconds(1.0 / 8); 15 | private const string animation = @"|/-\"; 16 | 17 | private readonly Timer timer; 18 | 19 | private double currentProgress = 0; 20 | private string currentText = string.Empty; 21 | private bool disposed = false; 22 | private int animationIndex = 0; 23 | 24 | public ProgressBar() 25 | { 26 | timer = new Timer(TimerHandler); 27 | 28 | // A progress bar is only for temporary display in a console window. 29 | // If the console output is redirected to a file, draw nothing. 30 | // Otherwise, we'll end up with a lot of garbage in the target file. 31 | if (!Console.IsOutputRedirected) 32 | { 33 | ResetTimer(); 34 | } 35 | } 36 | 37 | public void Report(double value) 38 | { 39 | // Make sure value is in [0..1] range 40 | value = Math.Max(0, Math.Min(1, value)); 41 | Interlocked.Exchange(ref currentProgress, value); 42 | } 43 | 44 | private void TimerHandler(object state) 45 | { 46 | lock (timer) 47 | { 48 | if (disposed) return; 49 | 50 | int progressBlockCount = (int)(currentProgress * blockCount); 51 | int percent = (int)(currentProgress * 100); 52 | string text = string.Format("[{0}{1}] {2,3}% {3}", 53 | new string('#', progressBlockCount), new string('-', blockCount - progressBlockCount), 54 | percent, 55 | animation[animationIndex++ % animation.Length]); 56 | UpdateText(text); 57 | 58 | ResetTimer(); 59 | } 60 | } 61 | 62 | private void UpdateText(string text) 63 | { 64 | // Get length of common portion 65 | int commonPrefixLength = 0; 66 | int commonLength = Math.Min(currentText.Length, text.Length); 67 | while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) 68 | { 69 | commonPrefixLength++; 70 | } 71 | 72 | // Backtrack to the first differing character 73 | StringBuilder outputBuilder = new StringBuilder(); 74 | outputBuilder.Append('\b', currentText.Length - commonPrefixLength); 75 | 76 | // Output new suffix 77 | outputBuilder.Append(text.Substring(commonPrefixLength)); 78 | 79 | // If the new text is shorter than the old one: delete overlapping characters 80 | int overlapCount = currentText.Length - text.Length; 81 | if (overlapCount > 0) 82 | { 83 | outputBuilder.Append(' ', overlapCount); 84 | outputBuilder.Append('\b', overlapCount); 85 | } 86 | 87 | Console.Write(outputBuilder); 88 | currentText = text; 89 | } 90 | 91 | private void ResetTimer() 92 | { 93 | timer.Change(animationInterval, TimeSpan.FromMilliseconds(-1)); 94 | } 95 | 96 | public void Dispose() 97 | { 98 | lock (timer) 99 | { 100 | disposed = true; 101 | UpdateText(string.Empty); 102 | } 103 | } 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tools/docker-swift/files/startmain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Make the rings if they don't exist already 5 | # 6 | 7 | # These can be set with docker run -e VARIABLE=X at runtime 8 | SWIFT_PART_POWER=${SWIFT_PART_POWER:-7} 9 | SWIFT_PART_HOURS=${SWIFT_PART_HOURS:-1} 10 | SWIFT_REPLICAS=${SWIFT_REPLICAS:-1} 11 | 12 | if [ -e /srv/account.builder ]; then 13 | echo "Ring files already exist in /srv, copying them to /etc/swift..." 14 | cp /srv/*.builder /etc/swift/ 15 | cp /srv/*.gz /etc/swift/ 16 | fi 17 | 18 | # This comes from a volume, so need to chown it here, not sure of a better way 19 | # to get it owned by Swift. 20 | chown -R swift:swift /srv 21 | 22 | if [ ! -e /etc/swift/account.builder ]; then 23 | 24 | cd /etc/swift 25 | 26 | # 2^& = 128 we are assuming just one drive 27 | # 1 replica only 28 | 29 | echo "No existing ring files, creating them..." 30 | 31 | swift-ring-builder object.builder create ${SWIFT_PART_POWER} ${SWIFT_REPLICAS} ${SWIFT_PART_HOURS} 32 | swift-ring-builder object.builder add r1z1-127.0.0.1:6010/sdb1 1 33 | swift-ring-builder object.builder rebalance 34 | swift-ring-builder container.builder create ${SWIFT_PART_POWER} ${SWIFT_REPLICAS} ${SWIFT_PART_HOURS} 35 | swift-ring-builder container.builder add r1z1-127.0.0.1:6011/sdb1 1 36 | swift-ring-builder container.builder rebalance 37 | swift-ring-builder account.builder create ${SWIFT_PART_POWER} ${SWIFT_REPLICAS} ${SWIFT_PART_HOURS} 38 | swift-ring-builder account.builder add r1z1-127.0.0.1:6012/sdb1 1 39 | swift-ring-builder account.builder rebalance 40 | 41 | # Back these up for later use 42 | echo "Copying ring files to /srv to save them if it's a docker volume..." 43 | cp *.gz /srv 44 | cp *.builder /srv 45 | 46 | fi 47 | 48 | # If you are going to put an ssl terminator in front of the proxy, then I believe 49 | # the storage_url_scheme should be set to https. So if this var isn't empty, set 50 | # the default storage url to https. 51 | if [ ! -z "${SWIFT_STORAGE_URL_SCHEME}" ]; then 52 | echo "Setting default_storage_scheme to https in proxy-server.conf..." 53 | sed -i -e "s/storage_url_scheme = default/storage_url_scheme = https/g" /etc/swift/proxy-server.conf 54 | grep "storage_url_scheme" /etc/swift/proxy-server.conf 55 | fi 56 | 57 | if [ ! -z "${SWIFT_SET_PASSWORDS}" ]; then 58 | echo "Setting passwords in /etc/swift/proxy-server.conf..." 59 | PASS=`pwgen 12 1` 60 | sed -i -e "s/user_admin_admin = admin .admin .reseller_admin/user_admin_admin = $PASS .admin .reseller_admin/g" /etc/swift/proxy-server.conf 61 | sed -i -e "s/user_test_tester = testing .admin/user_test_tester = $PASS .admin/g" /etc/swift/proxy-server.conf 62 | sed -i -e "s/user_test2_tester2 = testing2 .admin/user_test2_tester2 = $PASS .admin/g" /etc/swift/proxy-server.conf 63 | sed -i -e "s/user_test_tester3 = testing3/user_test_tester3 = $PASS/g" /etc/swift/proxy-server.conf 64 | grep "user_test" /etc/swift/proxy-server.conf 65 | fi 66 | 67 | # Start supervisord 68 | echo "Starting supervisord..." 69 | /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 70 | 71 | # Create default container 72 | if [ ! -z "${SWIFT_DEFAULT_CONTAINER}" ]; then 73 | echo "Creating default container..." 74 | for container in ${SWIFT_DEFAULT_CONTAINER} ; do 75 | echo "Creating container...${container}" 76 | swift -A http://localhost:8080/auth/v1.0 -U admin:admin -K admin post ${container} 77 | done 78 | fi 79 | 80 | # Create meta-url-key to allow temp download url generation 81 | if [ ! -z "${SWIFT_TEMP_URL_KEY}" ]; then 82 | echo "Setting X-Account-Meta-Temp-URL-Key..." 83 | swift -A http://localhost:8080/auth/v1.0 -U test:tester -K testing post -m "Temp-URL-Key:${SWIFT_TEMP_URL_KEY}" 84 | fi 85 | 86 | # 87 | # Tail the log file for "docker log $CONTAINER_ID" 88 | # 89 | 90 | # sleep waiting for rsyslog to come up under supervisord 91 | sleep 3 92 | 93 | echo "Starting to tail /var/log/syslog...(hit ctrl-c if you are starting the container in a bash shell)" 94 | 95 | tail -n 0 -f /var/log/syslog 96 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Commands/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SwiftClient.Cli 7 | { 8 | public static class ListCommand 9 | { 10 | public static int Run(ListOptions options, Client client) 11 | { 12 | if (string.IsNullOrEmpty(options.Container)) 13 | { 14 | var accountData = client.GetAccount().Result; 15 | if (accountData.IsSuccess) 16 | { 17 | if (accountData.Containers != null && accountData.Containers.Count > 0) 18 | { 19 | var list = new List(); 20 | foreach (var c in accountData.Containers) 21 | { 22 | list.Add(new SwiftContainer 23 | { 24 | Bytes = c.Bytes, 25 | Container = c.Container, 26 | Objects = c.Objects 27 | }); 28 | } 29 | 30 | var table = list.ToStringTable( 31 | u => u.Container, 32 | u => u.Objects, 33 | u => u.Size 34 | ); 35 | Console.WriteLine(table); 36 | } 37 | else 38 | { 39 | Console.WriteLine("No containers found"); 40 | } 41 | } 42 | else 43 | { 44 | Logger.LogError(accountData.Reason); 45 | } 46 | } 47 | else 48 | { 49 | var queryParams = new Dictionary(); 50 | 51 | if (!string.IsNullOrEmpty(options.Delimiter)) 52 | { 53 | queryParams.Add("delimiter", options.Delimiter); 54 | } 55 | 56 | if (!string.IsNullOrEmpty(options.Prefix)) 57 | { 58 | queryParams.Add("prefix", options.Prefix); 59 | } 60 | 61 | var containerData = client.GetContainer(options.Container, null, queryParams).Result; 62 | if (containerData.IsSuccess) 63 | { 64 | if (containerData.Objects != null && containerData.Objects.Count > 0) 65 | { 66 | var list = new List(); 67 | foreach (var c in containerData.Objects) 68 | { 69 | list.Add(new SwiftObject 70 | { 71 | Bytes = c.Bytes, 72 | ContentType = c.ContentType, 73 | Hash = c.Hash, 74 | LastModified = c.LastModified, 75 | Object = c.Object 76 | }); 77 | } 78 | 79 | var table = list.ToStringTable( 80 | u => u.Object, 81 | u => u.Size, 82 | u => u.LastModified, 83 | u => u.ContentType 84 | ); 85 | Console.WriteLine(table); 86 | } 87 | else 88 | { 89 | Console.WriteLine($"Container is empty"); 90 | } 91 | } 92 | else 93 | { 94 | Logger.LogError(containerData.Reason); 95 | } 96 | } 97 | return 0; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/SwiftClient/SwiftClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Http; 5 | 6 | using SwiftClient.Extensions; 7 | 8 | namespace SwiftClient 9 | { 10 | public partial class Client : ISwiftClient, IDisposable 11 | { 12 | public SwiftRetryManager RetryManager; 13 | 14 | protected ISwiftLogger _logger; 15 | protected HttpClient _client; 16 | protected bool _customClient; 17 | 18 | public Client() { } 19 | 20 | public Client(SwiftCredentials credentials) : this(new SwiftAuthManager(credentials)) { } 21 | 22 | public Client(SwiftCredentials credentials, ISwiftLogger logger) : this(credentials) 23 | { 24 | SetLogger(logger); 25 | } 26 | 27 | public Client(ISwiftAuthManager authManager) 28 | { 29 | if (authManager.Authenticate == null) 30 | { 31 | authManager.Authenticate = Authenticate; 32 | } 33 | 34 | RetryManager = new SwiftRetryManager(authManager); 35 | } 36 | 37 | public Client(ISwiftAuthManager authManager, ISwiftLogger logger) : this(authManager) 38 | { 39 | SetLogger(logger); 40 | } 41 | 42 | public void SetHttpClient(IHttpClientFactory clientFactory, bool noDispose = true) 43 | { 44 | _client = clientFactory.CreateClient("swift"); 45 | if (noDispose) 46 | { 47 | _customClient = true; 48 | } 49 | } 50 | 51 | private void FillRequest(HttpRequestMessage request, SwiftAuthData auth, Dictionary headers = null) 52 | { 53 | // set headers 54 | if (headers == null) 55 | { 56 | headers = new Dictionary(); 57 | } 58 | 59 | if (auth != null) 60 | { 61 | headers[SwiftHeaderKeys.AuthToken] = auth.AuthToken; 62 | } 63 | 64 | request.SetHeaders(headers); 65 | } 66 | 67 | private T GetExceptionResponse(Exception ex, string url) where T : SwiftBaseResponse, new() 68 | { 69 | var result = new T(); 70 | 71 | if (ex is WebException webException) 72 | { 73 | var rsp = ((HttpWebResponse)webException.Response); 74 | 75 | if (rsp != null) 76 | { 77 | result.StatusCode = rsp.StatusCode; 78 | result.Reason = rsp.StatusDescription; 79 | } 80 | } 81 | else 82 | { 83 | result.StatusCode = HttpStatusCode.BadRequest; 84 | result.Reason = ex.Message; 85 | } 86 | 87 | if (_logger != null) 88 | { 89 | _logger.LogRequestError(ex, result.StatusCode, result.Reason, url); 90 | } 91 | 92 | return result; 93 | } 94 | 95 | private T GetResponse(HttpResponseMessage rsp) where T : SwiftBaseResponse, new() => 96 | new T 97 | { 98 | StatusCode = rsp.StatusCode, 99 | Headers = rsp.Headers.ToDictionary(), 100 | Reason = rsp.ReasonPhrase, 101 | ContentLength = rsp.Content.Headers.ContentLength ?? 0 102 | }; 103 | 104 | bool disposed = false; 105 | 106 | public void Dispose() 107 | { 108 | Dispose(true); 109 | GC.SuppressFinalize(this); 110 | } 111 | 112 | protected virtual void Dispose(bool disposing) 113 | { 114 | if (disposed) 115 | return; 116 | 117 | if (disposing && !_customClient) 118 | { 119 | _client.Dispose(); 120 | } 121 | 122 | disposed = true; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/README.md: -------------------------------------------------------------------------------- 1 | # Swift Client CLI 2 | 3 | SwiftClient.Cli is a cross-platform console application compatible with DNX 4.5.1, it's main purpose is to transfer large objects into and out of OpenStack Swift. 4 | Large files are split into chunks of configurable size and uploaded to a temporary container, when all chunks are uploaded a merge operation is triggered on the server using Swift object manifest. 5 | The CLI also supports bulk upload of an entire directory tree, files are uploaded in parallel, the degree of parallelism is also configurable. 6 | 7 | # Commands 8 | 9 | SwiftClient.Cli will prompt for authentication if these environment variables are not present: 10 | 11 | ``` 12 | SWIFT_URL=http://localhost:8080 13 | SWIFT_USER=test:tester 14 | SWIFT_KEY=testing 15 | ``` 16 | 17 | ### Login 18 | 19 | ``` 20 | login -h -u -p 21 | ``` 22 | 23 | Usage: 24 | ``` 25 | login -h http://localhost:8080 -u test:tester -p testing 26 | ``` 27 | 28 | Result: 29 | ``` 30 | Connecting to http://localhost:8080 as test:tester 31 | Authentication token received from http://localhost:8080/v1/AUTH_test 32 | ``` 33 | 34 | ### Stats 35 | 36 | List account statistics: 37 | ``` 38 | stats 39 | ``` 40 | 41 | Result: 42 | ``` 43 | | ContainersCount | ObjectsCount | Size | 44 | |--------------------------------------------| 45 | | 6 | 143 | 367.80 MB | 46 | ``` 47 | 48 | ### List 49 | 50 | ``` 51 | ls -c 52 | ``` 53 | 54 | List containers: 55 | ``` 56 | ls 57 | ``` 58 | 59 | Result: 60 | ``` 61 | | Container | Objects | Size | 62 | |---------------------------------| 63 | | docs | 13 | 24.87 MB | 64 | | images | 117 | 26.25 MB | 65 | | videos | 8 | 205.29 MB | 66 | 67 | ``` 68 | 69 | List objects in a container: 70 | ``` 71 | ls -c docs 72 | ``` 73 | 74 | Result: 75 | ``` 76 | | Object | Size | LastModified | 77 | |------------------------------------------------------------------------------------------| 78 | | 2015/06/09/file-3c10cce7ca03453b87c65e75d4db850e.pdf | 88.48 KB | 10/27/2015 2:48:43 PM | 79 | | 2015/07/10/file-2283bb7c4318481cbbacbe87ac850bbc.docx | 18.32 KB | 10/27/2015 2:48:42 PM | 80 | | 2015/07/23/file-2737d45f2d674f36a47cb622eaff2be3.pptx | 5.30 MB | 10/27/2015 2:48:44 PM | 81 | ``` 82 | 83 | ### Put 84 | 85 | ``` 86 | put -c -o -b -p -f 87 | ``` 88 | 89 | Upload file to a container: 90 | ``` 91 | put -c videos -o test.mp4 -f "C:\vid12GB.mp4" 92 | ``` 93 | 94 | Upload file to a container with a 20MB chunk size: 95 | ``` 96 | put -c videos -o test.mp4 -b 20 -f "C:\vid2GB.mp4" 97 | ``` 98 | 99 | Result: 100 | ``` 101 | Uploaded 2 GB 102 | Upload done in 55 seconds 103 | ``` 104 | 105 | Upload a directory tree to a container: 106 | ``` 107 | put -c videos -f "C:\videos" 108 | ``` 109 | 110 | Upload a directory tree to a container using 10 parallel HTTP calls: 111 | ``` 112 | put -c videos -p 10 -f "C:\videos" 113 | ``` 114 | 115 | Sub-directories will be added to the object name `sub-dir1/sub-dir2/file`. 116 | 117 | Result: 118 | ``` 119 | Uploaded 20/20 120 | Upload done in 2 minutes 121 | ``` 122 | 123 | ### Get 124 | 125 | ``` 126 | get -c -o -b -f 127 | ``` 128 | 129 | Download file: 130 | ``` 131 | get -c videos -o test.mp4 -f "C:\my videos\my.mp4" 132 | ``` 133 | 134 | Download file with a 20MB buffer size: 135 | ``` 136 | get -c videos -o test.mp4 -b 20 -f "C:\my videos\my.mp4" 137 | ``` 138 | 139 | Result: 140 | ``` 141 | videos/test.mp4 downloaded to C:\my videos\my.mp4 142 | ``` 143 | 144 | ### Remove 145 | 146 | ``` 147 | get -c -o 148 | ``` 149 | 150 | Delete file from container: 151 | ``` 152 | rm -c videos -o test.mp4 153 | ``` 154 | 155 | Result: 156 | ``` 157 | videos/test.mp4 deleted 158 | ``` 159 | 160 | Delete container and objects: 161 | ``` 162 | rm -c videos 163 | ``` 164 | 165 | Result: 166 | ``` 167 | videos deleted 168 | ``` 169 | 170 | -------------------------------------------------------------------------------- /SwiftClient.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2260B1B4-9A09-425D-8A62-511C8AA95C78}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2A043E34-4F7B-43F8-8C2B-823309D51C23}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D2397301-E397-40ED-8307-A9D94C7E687A}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B791F94A-97EF-45EE-8E67-A43704F459B5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftClient", "src\SwiftClient\SwiftClient.csproj", "{C855AA03-FD6C-422F-99FA-4A0BFCB67F39}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftClient.Test", "test\SwiftClient.Test\SwiftClient.Test.csproj", "{5985B113-1A1C-4EFD-B06C-341C4B9467E9}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftClient.AspNetCore.Demo", "samples\SwiftClient.AspNetCore.Demo\SwiftClient.AspNetCore.Demo.csproj", "{B1D7D776-743F-4819-87F1-5DDFD3D26C07}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftClient.Cli", "samples\SwiftClient.Cli\SwiftClient.Cli.csproj", "{BA84D7D5-C30D-4097-A4D3-7BA574DB13BF}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftClient.AspNetCore", "src\SwiftClient.AspNetCore\SwiftClient.AspNetCore.csproj", "{EF6AAD2D-F8C5-491D-87F2-7F24733163CF}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {C855AA03-FD6C-422F-99FA-4A0BFCB67F39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {C855AA03-FD6C-422F-99FA-4A0BFCB67F39}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {C855AA03-FD6C-422F-99FA-4A0BFCB67F39}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {C855AA03-FD6C-422F-99FA-4A0BFCB67F39}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {5985B113-1A1C-4EFD-B06C-341C4B9467E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {5985B113-1A1C-4EFD-B06C-341C4B9467E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {5985B113-1A1C-4EFD-B06C-341C4B9467E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {5985B113-1A1C-4EFD-B06C-341C4B9467E9}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {B1D7D776-743F-4819-87F1-5DDFD3D26C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {B1D7D776-743F-4819-87F1-5DDFD3D26C07}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {B1D7D776-743F-4819-87F1-5DDFD3D26C07}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {B1D7D776-743F-4819-87F1-5DDFD3D26C07}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {BA84D7D5-C30D-4097-A4D3-7BA574DB13BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {BA84D7D5-C30D-4097-A4D3-7BA574DB13BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {BA84D7D5-C30D-4097-A4D3-7BA574DB13BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {EF6AAD2D-F8C5-491D-87F2-7F24733163CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {EF6AAD2D-F8C5-491D-87F2-7F24733163CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {EF6AAD2D-F8C5-491D-87F2-7F24733163CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {EF6AAD2D-F8C5-491D-87F2-7F24733163CF}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(NestedProjects) = preSolution 54 | {C855AA03-FD6C-422F-99FA-4A0BFCB67F39} = {2260B1B4-9A09-425D-8A62-511C8AA95C78} 55 | {5985B113-1A1C-4EFD-B06C-341C4B9467E9} = {D2397301-E397-40ED-8307-A9D94C7E687A} 56 | {EF6AAD2D-F8C5-491D-87F2-7F24733163CF} = {2260B1B4-9A09-425D-8A62-511C8AA95C78} 57 | EndGlobalSection 58 | GlobalSection(ExtensibilityGlobals) = postSolution 59 | SolutionGuid = {1BEFDF8E-3F30-4DF5-A084-C622770C11BC} 60 | EndGlobalSection 61 | EndGlobal 62 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | 2 | var ctrl = function () { 3 | this.initFileUpload(); 4 | this.initTree(); 5 | this.initVideo(); 6 | }; 7 | 8 | ctrl.prototype.initTree = function () { 9 | $.Mustache.addFromDom(); 10 | 11 | this.renderTree($('#tree').data('tree')); 12 | 13 | $('#tree').on('click', '.js-treeToggle', function (e) { 14 | e.preventDefault(); 15 | var $target = $(e.currentTarget), 16 | $ul = $target.siblings('ul'); 17 | 18 | if ($ul.is(":visible")) { 19 | $ul.hide(); 20 | $target.addClass('glyphicon-plus').removeClass('glyphicon-minus'); 21 | } else { 22 | $ul.show(); 23 | $target.addClass('glyphicon-minus').removeClass('glyphicon-plus'); 24 | } 25 | }); 26 | }; 27 | 28 | ctrl.prototype.renderTree = function (data) { 29 | var $html = $.Mustache.render('tree-template', data); 30 | $('#tree').html($html); 31 | }; 32 | 33 | ctrl.prototype.refreshTree = function () { 34 | $.ajax({ 35 | url: 'home/refreshtree', 36 | data: { }, 37 | dataType: 'json', 38 | success: (function (response) { 39 | if (response.data) { 40 | this.renderTree(response.data); 41 | this.initVideo(); 42 | } 43 | }).bind(this) 44 | }); 45 | }; 46 | 47 | ctrl.prototype.initFileUpload = function () { 48 | 49 | this.resetIndex(); 50 | 51 | $('#fileupload').fileupload({ 52 | dataType: 'json', 53 | maxChunkSize: 2000000, 54 | add: this.onFileAdd.bind(this), 55 | progress: this.uploadProgress.bind(this), 56 | formData: this.getFormData.bind(this), 57 | done: this.fileUploadDone.bind(this) 58 | }); 59 | }; 60 | 61 | ctrl.prototype.onFileAdd = function (e, data) { 62 | if (data.files != null && data.files[0] != null) { 63 | data.submit(); 64 | } 65 | return false; 66 | }; 67 | 68 | ctrl.prototype.getFormData = function () { 69 | this.currentChunkIndex++; 70 | return [{ 71 | name: 'segment', 72 | value: this.currentChunkIndex 73 | }] 74 | }; 75 | 76 | ctrl.prototype.uploadProgress = function (e, data) { 77 | var progress = (data.loaded / data.total * 100).toFixed(2) + '%'; 78 | $('.js-uploadStatus').html('Uploaded ' + progress + ' (' + this.formatSize(data.loaded) + ' of ' + this.formatSize(data.total) + ')'); 79 | $('.js-uploadStatus').css('width', progress); 80 | $('.js-uploadStatus').parents('.js-uploadStatusContainer').show(); 81 | }; 82 | 83 | ctrl.prototype.formatSize = function (bytes) { 84 | if (typeof bytes !== 'number') { 85 | return ''; 86 | } 87 | if (bytes >= 1000000000) { 88 | return (bytes / 1000000000).toFixed(2) + ' GB'; 89 | } 90 | if (bytes >= 1000000) { 91 | return (bytes / 1000000).toFixed(2) + ' MB'; 92 | } 93 | return (bytes / 1000).toFixed(2) + ' KB'; 94 | }; 95 | 96 | ctrl.prototype.fileUploadDone = function (e, data) { 97 | 98 | $('.js-uploadStatus').html('File chunks uploaded in temporary container! Please wait for transfer and cleanup...'); 99 | 100 | if (data.result.success) { 101 | $.ajax({ 102 | url: 'home/uploaddone', 103 | data: { 104 | segmentsCount: this.currentChunkIndex, 105 | fileName: data.result.fileName, 106 | contentType: data.result.contentType 107 | }, 108 | dataType: 'json', 109 | success: (function (response) { 110 | 111 | $('.js-uploadStatus').html(''); 112 | $('.js-uploadStatus').parents('.js-uploadStatusContainer').hide(); 113 | 114 | this.refreshTree(); 115 | }).bind(this) 116 | }); 117 | } 118 | 119 | this.resetIndex(); 120 | }; 121 | 122 | ctrl.prototype.resetIndex = function () { 123 | this.currentChunkIndex = -1; 124 | }; 125 | 126 | ctrl.prototype.initVideo = function () { 127 | var $video = $('.js-video'); 128 | 129 | if ($video.length) { 130 | 131 | var videoPlayer = videojs($video.get(0), { 132 | controls: true, 133 | preload: 'none', 134 | width: 640, 135 | height: 360, 136 | }, function () { 137 | }); 138 | } 139 | }; 140 | 141 | $(function () { 142 | 143 | var handler = new ctrl(); 144 | 145 | }); 146 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/AuthManager.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SwiftClient.Cli 8 | { 9 | public class AuthManager 10 | { 11 | bool validLoginData = false; 12 | Client client = null; 13 | SwiftCredentials credentials = new SwiftCredentials(); 14 | 15 | public AuthManager(string[] args) 16 | { 17 | if(args.Length > 0 && ParseLoginCommand(args) == 0) 18 | { 19 | validLoginData = true; 20 | } 21 | } 22 | 23 | public async Task Connect() 24 | { 25 | var needsAuth = validLoginData ? false : !ValidateLogin(); 26 | if (needsAuth) 27 | { 28 | while (needsAuth) 29 | { 30 | needsAuth = !PromptLogin(); 31 | needsAuth = !await DoLogin(); 32 | } 33 | } 34 | else 35 | { 36 | needsAuth = !await DoLogin(); 37 | while (needsAuth) 38 | { 39 | needsAuth = !PromptLogin(); 40 | needsAuth = !await DoLogin(); 41 | } 42 | } 43 | 44 | return client; 45 | } 46 | 47 | public SwiftCredentials Credentials() 48 | { 49 | return credentials; 50 | } 51 | 52 | private bool ValidateLogin() 53 | { 54 | bool exists = true; 55 | 56 | var endpoint = Environment.GetEnvironmentVariable("SWIFT_URL"); 57 | var username = Environment.GetEnvironmentVariable("SWIFT_USER"); 58 | var password = Environment.GetEnvironmentVariable("SWIFT_KEY"); 59 | 60 | if (string.IsNullOrEmpty(endpoint)) 61 | { 62 | Console.WriteLine("SWIFT_URL environment variable not set"); 63 | exists = false; 64 | } 65 | else 66 | { 67 | credentials.Endpoints = new List { endpoint }; 68 | } 69 | 70 | if (string.IsNullOrEmpty(username)) 71 | { 72 | Console.WriteLine("SWIFT_USER environment variable not set"); 73 | exists = false; 74 | } 75 | else 76 | { 77 | credentials.Username = username; 78 | } 79 | 80 | if (string.IsNullOrEmpty(password)) 81 | { 82 | Console.WriteLine("SWIFT_KEY environment variable not set"); 83 | exists = false; 84 | } 85 | else 86 | { 87 | credentials.Password = password; 88 | } 89 | 90 | return exists; 91 | } 92 | 93 | private bool PromptLogin() 94 | { 95 | Console.WriteLine($"Connect to swift using command:"); 96 | Console.WriteLine("login -h -u -p "); 97 | var loginCommand = Console.ReadLine(); 98 | 99 | var exitCode = ParseLoginCommand(loginCommand.ParseArguments()); 100 | 101 | return exitCode == 0; 102 | } 103 | 104 | private int ParseLoginCommand(string[] args) 105 | { 106 | return CommandLine.Parser.Default.ParseArguments(args).MapResult( 107 | options => { 108 | credentials.Endpoints = new List { options.Endpoint }; 109 | credentials.Username = options.User; 110 | credentials.Password = options.Password; 111 | return 0; 112 | }, 113 | errs => 1); 114 | } 115 | 116 | private async Task DoLogin() 117 | { 118 | bool ok = false; 119 | 120 | Console.WriteLine($"Connecting to {credentials.Endpoints.FirstOrDefault()} as {credentials.Username}"); 121 | 122 | client = new Client(new SwiftAuthManager(credentials)) 123 | .SetRetryCount(1) 124 | .SetLogger(new SwiftConsoleLog()); 125 | 126 | var data = await client.Authenticate(); 127 | 128 | if (data != null) 129 | { 130 | ok = true; 131 | Console.WriteLine($"Authentication token received from {data.StorageUrl}"); 132 | } 133 | 134 | return ok; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tools/docker-swift/README.md: -------------------------------------------------------------------------------- 1 | # OpenStack Swift dev/test all-in-one container 2 | 3 | Docker file forked from [docker-swift-onlyone](https://github.com/MorrisJobke/docker-swift-onlyone) 4 | 5 | Install from docker hub: 6 | 7 | ```bash 8 | docker run -v /srv --name SWIFT_DATA busybox 9 | docker run --restart=unless-stopped --name SWIFT_AIO -d -p 8080:8080 --volumes-from SWIFT_DATA -t morrisjobke/docker-swift-onlyone 10 | ``` 11 | 12 | Test on windows: 13 | ```bash 14 | Invoke-RestMethod -Method Get -Headers @{'X-Auth-User'= 'test:tester';'X-Auth-Key'='testing'} -Uri http://localhost:8080/auth/v1.0/ 15 | ``` 16 | 17 | Test on linux: 18 | ```bash 19 | curl -i -H "X-Auth-User:test:tester" -H "X-Auth-Key:testing" http://localhost:8080/auth/v1.0/ 20 | ``` 21 | 22 | Tear down: 23 | ```bash 24 | docker stop SWIFT_AIO 25 | docker rm SWIFT_AIO 26 | docker rm SWIFT_DATA 27 | ``` 28 | 29 | ## Windows Prerequisites 30 | 31 | Install [Docker for Windows](https://docs.docker.com/docker-for-windows/), works on Windows 10 64bit only. 32 | 33 | ## Run OpenStack Swift docker image 34 | 35 | You'll need to copy all files inside `vtfuture/SwiftClient/tools/docker-swift` and run `up.ps1`, this will build and start a swift container that exposes port 8080 on your windows. 36 | 37 | Optionally you could follow these steps: 38 | 39 | ***Download project using git*** 40 | 41 | ```bash 42 | git clone https://github.com/vtfuture/SwiftClient 43 | ``` 44 | 45 | ***Build docker image*** 46 | 47 | ```bash 48 | # navigate to docker-swift folder and build local 49 | docker build -t swift-aio . 50 | ``` 51 | 52 | ***Build a storage container*** 53 | 54 | ```bash 55 | docker run -v /srv --name SWIFT_DATA busybox 56 | ``` 57 | 58 | ***Start swift container in background*** 59 | 60 | ```bash 61 | docker run --name SWIFT_AIO -d -p 8080:8080 --volumes-from SWIFT_DATA -t swift-aio 62 | ``` 63 | 64 | ***Test connectivity*** 65 | 66 | ```bash 67 | Invoke-RestMethod -Method Get -Headers @{'X-Auth-User'= 'test:tester';'X-Auth-Key'='testing'} -Uri http://localhost:8080/auth/v1.0/ 68 | ``` 69 | 70 | ## Tear down 71 | 72 | Run `down.ps1` or the following commands to stop and remove swift containers and image: 73 | 74 | ```bash 75 | docker stop SWIFT_AIO 76 | docker rm SWIFT_AIO 77 | docker rm SWIFT_DATA 78 | docker rmi swift-aio 79 | ``` 80 | 81 | ## Linux Prerequisites 82 | 83 | OpenStack Swift docker is compatible with Ubuntu 14.04.3 LTS or newer. 84 | 85 | ### Install docker engine 86 | 87 | ```bash 88 | #!/bin/bash 89 | 90 | if hash docker 2>/dev/null; then 91 | echo ">>> Docker detected, skyping prerequisites and install" 92 | else 93 | # docker prerequisites 94 | apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 95 | AptDockerFile = "/etc/apt/sources.list.d/docker.list" 96 | echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" > $AptDockerFile 97 | apt-get update 98 | 99 | # install docker 100 | apt-get -y install docker-engine 101 | echo ">>> Docker engine installed" 102 | fi 103 | ``` 104 | 105 | Optionally you could add your user to the `docker` group in order to run docker commands without `sudo`. 106 | 107 | ``` 108 | sudo usermod -aG docker your-username 109 | ``` 110 | 111 | ## Run OpenStack Swift docker image 112 | 113 | You'll need to copy all files inside `vtfuture/SwiftClient/tools/docker-swift` on your Ubuntu server and run `up.sh`, this will build and start a swift container that exposes port 8080 on your server. 114 | 115 | Optionally you could follow these steps: 116 | 117 | ***Download project using git*** 118 | 119 | ```bash 120 | cd ~/ 121 | git clone https://github.com/vtfuture/SwiftClient 122 | ``` 123 | 124 | ***Build docker image*** 125 | 126 | ```bash 127 | # navigate to docker-swift folder 128 | cd ~/SwiftClient/docker-swift 129 | 130 | # Build local 131 | docker build -t swift-aio . 132 | ``` 133 | 134 | ***Build a storage container*** 135 | 136 | ```bash 137 | docker run -v /srv --name SWIFT_DATA busybox 138 | ``` 139 | 140 | ***Start swift container in background*** 141 | 142 | ```bash 143 | docker run --name SWIFT_AIO -d -p 8080:8080 --volumes-from SWIFT_DATA -t swift-aio 144 | ``` 145 | 146 | ***Test connectivity*** 147 | 148 | ```bash 149 | curl -i -H "X-Auth-User:test:tester" -H "X-Auth-Key:testing" http://localhost:8080/auth/v1.0/ 150 | ``` 151 | 152 | ## Tear down 153 | 154 | Run `down.sh` or the following commands to stop and remove swift containers and image from your server: 155 | 156 | ```bash 157 | docker stop SWIFT_AIO 158 | docker rm SWIFT_AIO 159 | docker rm SWIFT_DATA 160 | docker rmi swift-aio 161 | ``` 162 | -------------------------------------------------------------------------------- /samples/SwiftClient.Cli/Extensions/TableParserExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace SwiftClient.Cli 11 | { 12 | public static class TableParserExtensions 13 | { 14 | public static string ToStringTable(this IEnumerable values, string[] columnHeaders, params Func[] valueSelectors) 15 | { 16 | return ToStringTable(values.ToArray(), columnHeaders, valueSelectors); 17 | } 18 | 19 | public static string ToStringTable(this T[] values, string[] columnHeaders, params Func[] valueSelectors) 20 | { 21 | Debug.Assert(columnHeaders.Length == valueSelectors.Length); 22 | 23 | var arrValues = new string[values.Length + 1, valueSelectors.Length]; 24 | 25 | // Fill headers 26 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) 27 | { 28 | arrValues[0, colIndex] = columnHeaders[colIndex]; 29 | } 30 | 31 | // Fill table rows 32 | for (int rowIndex = 1; rowIndex < arrValues.GetLength(0); rowIndex++) 33 | { 34 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) 35 | { 36 | object value = valueSelectors[colIndex].Invoke(values[rowIndex - 1]); 37 | 38 | arrValues[rowIndex, colIndex] = value != null ? value.ToString() : "null"; 39 | } 40 | } 41 | 42 | return ToStringTable(arrValues); 43 | } 44 | 45 | public static string ToStringTable(this string[,] arrValues) 46 | { 47 | int[] maxColumnsWidth = GetMaxColumnsWidth(arrValues); 48 | var headerSpliter = new string('-', maxColumnsWidth.Sum(i => i + 3) - 1); 49 | 50 | var sb = new StringBuilder(); 51 | for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++) 52 | { 53 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) 54 | { 55 | // Print cell 56 | string cell = arrValues[rowIndex, colIndex]; 57 | cell = cell.PadRight(maxColumnsWidth[colIndex]); 58 | sb.Append(" | "); 59 | sb.Append(cell); 60 | } 61 | 62 | // Print end of line 63 | sb.Append(" | "); 64 | sb.AppendLine(); 65 | 66 | // Print splitter 67 | if (rowIndex == 0) 68 | { 69 | sb.AppendFormat(" |{0}| ", headerSpliter); 70 | sb.AppendLine(); 71 | } 72 | } 73 | 74 | return sb.ToString(); 75 | } 76 | 77 | private static int[] GetMaxColumnsWidth(string[,] arrValues) 78 | { 79 | var maxColumnsWidth = new int[arrValues.GetLength(1)]; 80 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) 81 | { 82 | for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++) 83 | { 84 | int newLength = arrValues[rowIndex, colIndex].Length; 85 | int oldLength = maxColumnsWidth[colIndex]; 86 | 87 | if (newLength > oldLength) 88 | { 89 | maxColumnsWidth[colIndex] = newLength; 90 | } 91 | } 92 | } 93 | 94 | return maxColumnsWidth; 95 | } 96 | 97 | public static string ToStringTable(this IEnumerable values, params Expression>[] valueSelectors) 98 | { 99 | var headers = valueSelectors.Select(func => GetProperty(func).Name).ToArray(); 100 | var selectors = valueSelectors.Select(exp => exp.Compile()).ToArray(); 101 | return ToStringTable(values, headers, selectors); 102 | } 103 | 104 | private static PropertyInfo GetProperty(Expression> expresstion) 105 | { 106 | if (expresstion.Body is UnaryExpression) 107 | { 108 | if ((expresstion.Body as UnaryExpression).Operand is MemberExpression) 109 | { 110 | return ((expresstion.Body as UnaryExpression).Operand as MemberExpression).Member as PropertyInfo; 111 | } 112 | } 113 | 114 | if ((expresstion.Body is MemberExpression)) 115 | { 116 | return (expresstion.Body as MemberExpression).Member as PropertyInfo; 117 | } 118 | return null; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /samples/SwiftClient.AspNetCore.Demo/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - SwiftClient.AspNetCore.Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | 44 |
45 | @RenderBody() 46 |
47 |
48 |

© 2016 - Veritech Solutions

49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 69 | 73 | 77 | 78 | 79 | 80 | 84 | 85 | 86 | 87 | 88 | 89 | @RenderSection("scripts", required: false) 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/SwiftClient/RetryPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using SwiftClient.Extensions; 5 | using System.Threading.Tasks; 6 | 7 | namespace SwiftClient 8 | { 9 | public class RetryPolicy 10 | { 11 | protected int _iRetryGlobal; 12 | protected int _nRetryGlobal; 13 | 14 | protected RetryPolicy() 15 | { 16 | _iRetryGlobal = 0; 17 | } 18 | 19 | public RetryPolicy WithCount(int retryCount) 20 | { 21 | if (retryCount < 1) throw new ArgumentOutOfRangeException("retryCount"); 22 | 23 | _nRetryGlobal = retryCount; 24 | 25 | return this; 26 | } 27 | 28 | public bool Do(Func func) 29 | { 30 | if (_iRetryGlobal < _nRetryGlobal) 31 | { 32 | var success = func(); 33 | 34 | if (success) return true; 35 | 36 | _iRetryGlobal++; 37 | 38 | return Do(func); 39 | } 40 | 41 | return false; 42 | } 43 | 44 | public async Task DoAsync(Func> func) 45 | { 46 | if (_iRetryGlobal < _nRetryGlobal) 47 | { 48 | var success = await func().ConfigureAwait(false); 49 | 50 | if (success) return true; 51 | 52 | _iRetryGlobal++; 53 | 54 | return await DoAsync(func).ConfigureAwait(false); 55 | } 56 | 57 | return false; 58 | } 59 | 60 | public static RetryPolicy Create() 61 | { 62 | return new RetryPolicy().WithCount(1); 63 | } 64 | } 65 | 66 | public class RetryPolicy : RetryPolicy 67 | { 68 | protected List _steps; 69 | protected static object _syncLock = new object(); 70 | protected int _iRetryStep; 71 | protected int _nRetryPerStep; 72 | 73 | protected RetryPolicy() : base() 74 | { 75 | _iRetryStep = 1; 76 | } 77 | 78 | public new RetryPolicy WithCount(int retryCount) 79 | { 80 | if (retryCount < 1) throw new ArgumentOutOfRangeException("retryCount"); 81 | 82 | _nRetryGlobal = retryCount; 83 | 84 | return this; 85 | } 86 | 87 | public RetryPolicy WithSteps(List steps) 88 | { 89 | if (steps == null || !steps.Any()) throw new ArgumentOutOfRangeException("steps"); 90 | 91 | _steps = steps; 92 | 93 | return this; 94 | } 95 | 96 | public RetryPolicy WithCountPerStep(int retryCount) 97 | { 98 | if (retryCount < 1) throw new ArgumentOutOfRangeException("retryCount"); 99 | 100 | _nRetryPerStep = retryCount; 101 | 102 | return this; 103 | } 104 | 105 | public bool Do(Func func) 106 | { 107 | return Do(() => 108 | { 109 | var retrier = RetryPolicy.Create().WithCount(_nRetryPerStep); 110 | 111 | var success = retrier.Do(() => 112 | { 113 | return func(_steps.FirstOrDefault()); 114 | }); 115 | 116 | if (success) return true; 117 | 118 | var count = _steps.Count(); 119 | 120 | if (_iRetryStep < count) 121 | { 122 | lock (_syncLock) 123 | { 124 | _steps.MoveFirstToLast(); 125 | } 126 | 127 | _iRetryStep++; 128 | 129 | return Do(func); 130 | } 131 | 132 | return false; 133 | }); 134 | } 135 | 136 | public Task DoAsync(Func> func) 137 | { 138 | return DoAsync(async () => 139 | { 140 | var retrier = RetryPolicy.Create().WithCount(_nRetryPerStep); 141 | 142 | var success = await retrier.DoAsync(async () => 143 | { 144 | return await func(_steps.FirstOrDefault()).ConfigureAwait(false); 145 | }).ConfigureAwait(false); 146 | 147 | if (success) return true; 148 | 149 | var count = _steps.Count(); 150 | 151 | if (_iRetryStep < count) 152 | { 153 | lock (_syncLock) 154 | { 155 | _steps.MoveFirstToLast(); 156 | } 157 | 158 | _iRetryStep++; 159 | 160 | return await DoAsync(func).ConfigureAwait(false); 161 | } 162 | 163 | return false; 164 | }); 165 | } 166 | 167 | public List GetSteps() 168 | { 169 | return _steps; 170 | } 171 | 172 | public new static RetryPolicy Create() 173 | { 174 | return new RetryPolicy().WithCount(1).WithCountPerStep(1); 175 | } 176 | } 177 | } 178 | --------------------------------------------------------------------------------