Skip to content

Commit 9caa655

Browse files
ElevenLabs-DotNet 2.2.0 (#41)
- Changed ElevenLabsClient to be IDisposable - The ElevenLabsClient must now be disposed if you do not pass your own HttpClient - Updated ElevenLabsClientSettings to accept custom domains - Added filesystemless overloads for uploading audio clips ElevenLabs-DotNet-Proxy 2.2.0 - Updated implementation to include WebApplication builder --------- Co-authored-by: Stillkill <36937920+RealStillkill@users.noreply.github.com>
1 parent cce4288 commit 9caa655

File tree

13 files changed

+363
-101
lines changed

13 files changed

+363
-101
lines changed
Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFramework>net6.0</TargetFramework>
4-
<ImplicitUsings>disable</ImplicitUsings>
5-
<Nullable>disable</Nullable>
6-
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
7-
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
8-
<SignAssembly>false</SignAssembly>
9-
<Authors>Stephen Hodgson</Authors>
10-
<Product>ElevenLabs-DotNet-Proxy</Product>
11-
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description>
12-
<Copyright>2023</Copyright>
13-
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl>
14-
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl>
15-
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags>
16-
<Title>ElevenLabs API Proxy</Title>
17-
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
18-
<Version>1.0.1</Version>
19-
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
20-
<PackageReleaseNotes>Initial Release!</PackageReleaseNotes>
21-
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
22-
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
23-
<PackageReadmeFile>Readme.md</PackageReadmeFile>
24-
<IncludeSymbols>True</IncludeSymbols>
25-
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
26-
<PackageLicenseFile>LICENSE</PackageLicenseFile>
27-
</PropertyGroup>
28-
<ItemGroup>
29-
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
30-
<FrameworkReference Include="Microsoft.AspNetCore.App" />
31-
</ItemGroup>
32-
<ItemGroup>
33-
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png">
34-
<Pack>True</Pack>
35-
<PackagePath>\</PackagePath>
36-
</None>
37-
<None Include="..\LICENSE">
38-
<Pack>True</Pack>
39-
<PackagePath>\</PackagePath>
40-
</None>
41-
</ItemGroup>
42-
<ItemGroup>
43-
<None Update="Readme.md">
44-
<Pack>True</Pack>
45-
<PackagePath>\</PackagePath>
46-
</None>
47-
</ItemGroup>
2+
<PropertyGroup>
3+
<TargetFramework>net6.0</TargetFramework>
4+
<ImplicitUsings>disable</ImplicitUsings>
5+
<Nullable>disable</Nullable>
6+
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
7+
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
8+
<SignAssembly>false</SignAssembly>
9+
<Authors>Stephen Hodgson</Authors>
10+
<Product>ElevenLabs-DotNet-Proxy</Product>
11+
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description>
12+
<Copyright>2024</Copyright>
13+
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl>
14+
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl>
15+
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags>
16+
<Title>ElevenLabs API Proxy</Title>
17+
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
18+
<Version>1.2.0</Version>
19+
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
20+
<PackageReleaseNotes>Initial Release!</PackageReleaseNotes>
21+
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
22+
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
23+
<PackageReadmeFile>Readme.md</PackageReadmeFile>
24+
<IncludeSymbols>True</IncludeSymbols>
25+
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
26+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
27+
</PropertyGroup>
28+
<ItemGroup>
29+
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
30+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
31+
</ItemGroup>
32+
<ItemGroup>
33+
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png">
34+
<Pack>True</Pack>
35+
<PackagePath>\</PackagePath>
36+
</None>
37+
<None Include="..\LICENSE">
38+
<Pack>True</Pack>
39+
<PackagePath>\</PackagePath>
40+
</None>
41+
</ItemGroup>
42+
<ItemGroup>
43+
<None Update="Readme.md">
44+
<Pack>True</Pack>
45+
<PackagePath>\</PackagePath>
46+
</None>
47+
</ItemGroup>
4848
</Project>

ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Licensed under the MIT License. See LICENSE in the project root for license information.
22

3+
using System.Threading.Tasks;
34
using Microsoft.AspNetCore.Http;
45

56
namespace ElevenLabs.Proxy
@@ -9,5 +10,8 @@ public abstract class AbstractAuthenticationFilter : IAuthenticationFilter
910
{
1011
/// <inheritdoc />
1112
public abstract void ValidateAuthentication(IHeaderDictionary request);
13+
14+
/// <inheritdoc />
15+
public abstract Task ValidateAuthenticationAsync(IHeaderDictionary request);
1216
}
1317
}

ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNetCore.Builder;
44
using Microsoft.AspNetCore.Hosting;
55
using Microsoft.AspNetCore.Http;
6+
using Microsoft.AspNetCore.Server.Kestrel.Core;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Hosting;
89
using Microsoft.Net.Http.Headers;
@@ -26,7 +27,7 @@ public class ElevenLabsProxyStartup
2627
private IAuthenticationFilter authenticationFilter;
2728

2829
// Copied from https://github.com/microsoft/reverse-proxy/blob/51d797986b1fea03500a1ad173d13a1176fb5552/src/ReverseProxy/Forwarder/RequestUtilities.cs#L61-L83
29-
private static readonly HashSet<string> ExcludedHeaders = new HashSet<string>()
30+
private static readonly HashSet<string> excludedHeaders = new()
3031
{
3132
HeaderNames.Connection,
3233
HeaderNames.TransferEncoding,
@@ -49,7 +50,12 @@ public class ElevenLabsProxyStartup
4950
#endif
5051
};
5152

52-
public void ConfigureServices(IServiceCollection services) { }
53+
/// <summary>
54+
/// Configures the <see cref="elevenLabsClient"/> and <see cref="IAuthenticationFilter"/> services.
55+
/// </summary>
56+
/// <param name="services"></param>
57+
public void ConfigureServices(IServiceCollection services)
58+
=> SetupServices(services.BuildServiceProvider());
5359

5460
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
5561
{
@@ -58,8 +64,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
5864
app.UseDeveloperExceptionPage();
5965
}
6066

61-
elevenLabsClient = app.ApplicationServices.GetRequiredService<ElevenLabsClient>();
62-
authenticationFilter = app.ApplicationServices.GetRequiredService<IAuthenticationFilter>();
67+
SetupServices(app.ApplicationServices);
6368

6469
app.UseHttpsRedirection();
6570
app.UseRouting();
@@ -77,25 +82,43 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
7782
/// <param name="args">Startup args.</param>
7883
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
7984
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
80-
{
81-
return Host.CreateDefaultBuilder(args)
85+
=> Host.CreateDefaultBuilder(args)
8286
.ConfigureWebHostDefaults(webBuilder =>
8387
{
8488
webBuilder.UseStartup<ElevenLabsProxyStartup>();
85-
webBuilder.ConfigureKestrel(options =>
86-
{
87-
options.AllowSynchronousIO = false;
88-
options.Limits.MinRequestBodyDataRate = null;
89-
options.Limits.MinResponseDataRate = null;
90-
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
91-
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
92-
});
89+
webBuilder.ConfigureKestrel(ConfigureKestrel);
9390
})
9491
.ConfigureServices(services =>
9592
{
9693
services.AddSingleton(elevenLabsClient);
9794
services.AddSingleton<IAuthenticationFilter, T>();
9895
}).Build();
96+
97+
public static WebApplication CreateWebApplication<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
98+
{
99+
var builder = WebApplication.CreateBuilder(args);
100+
builder.WebHost.ConfigureKestrel(ConfigureKestrel);
101+
builder.Services.AddSingleton(elevenLabsClient);
102+
builder.Services.AddSingleton<IAuthenticationFilter, T>();
103+
var app = builder.Build();
104+
var startup = new ElevenLabsProxyStartup();
105+
startup.Configure(app, app.Environment);
106+
return app;
107+
}
108+
109+
private static void ConfigureKestrel(KestrelServerOptions options)
110+
{
111+
options.AllowSynchronousIO = false;
112+
options.Limits.MinRequestBodyDataRate = null;
113+
options.Limits.MinResponseDataRate = null;
114+
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
115+
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
116+
}
117+
118+
private void SetupServices(IServiceProvider serviceProvider)
119+
{
120+
elevenLabsClient = serviceProvider.GetRequiredService<ElevenLabsClient>();
121+
authenticationFilter = serviceProvider.GetRequiredService<IAuthenticationFilter>();
99122
}
100123

101124
private static async Task HealthEndpoint(HttpContext context)
@@ -115,31 +138,34 @@ private async Task HandleRequest(HttpContext httpContext, string endpoint)
115138
{
116139
try
117140
{
141+
// ReSharper disable once MethodHasAsyncOverload
142+
// just in case either method is implemented we call it twice.
118143
authenticationFilter.ValidateAuthentication(httpContext.Request.Headers);
144+
await authenticationFilter.ValidateAuthenticationAsync(httpContext.Request.Headers);
119145

120146
var method = new HttpMethod(httpContext.Request.Method);
121147
var uri = new Uri(string.Format(elevenLabsClient.ElevenLabsClientSettings.BaseRequestUrlFormat, $"{endpoint}{httpContext.Request.QueryString}"));
122-
var elevenLabsRequest = new HttpRequestMessage(method, uri);
148+
using var request = new HttpRequestMessage(method, uri);
123149

124-
elevenLabsRequest.Content = new StreamContent(httpContext.Request.Body);
150+
request.Content = new StreamContent(httpContext.Request.Body);
125151

126152
if (httpContext.Request.ContentType != null)
127153
{
128-
elevenLabsRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType);
154+
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType);
129155
}
130156

131-
var proxyResponse = await elevenLabsClient.Client.SendAsync(elevenLabsRequest, HttpCompletionOption.ResponseHeadersRead);
157+
var proxyResponse = await elevenLabsClient.Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
132158
httpContext.Response.StatusCode = (int)proxyResponse.StatusCode;
133159

134160
foreach (var (key, value) in proxyResponse.Headers)
135161
{
136-
if (ExcludedHeaders.Contains(key)) { continue; }
162+
if (excludedHeaders.Contains(key)) { continue; }
137163
httpContext.Response.Headers[key] = value.ToArray();
138164
}
139165

140166
foreach (var (key, value) in proxyResponse.Content.Headers)
141167
{
142-
if (ExcludedHeaders.Contains(key)) { continue; }
168+
if (excludedHeaders.Contains(key)) { continue; }
143169
httpContext.Response.Headers[key] = value.ToArray();
144170
}
145171

ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Licensed under the MIT License. See LICENSE in the project root for license information.
22

3-
using System.Security.Authentication;
43
using Microsoft.AspNetCore.Http;
4+
using System.Security.Authentication;
5+
using System.Threading.Tasks;
56

67
namespace ElevenLabs.Proxy
78
{
@@ -17,5 +18,13 @@ public interface IAuthenticationFilter
1718
/// <param name="request"></param>
1819
/// <exception cref="AuthenticationException"></exception>
1920
void ValidateAuthentication(IHeaderDictionary request);
21+
22+
/// <summary>
23+
/// Checks the headers for your user issued token.
24+
/// If it's not valid, then throw <see cref="AuthenticationException"/>.
25+
/// </summary>
26+
/// <param name="request"></param>
27+
/// <exception cref="AuthenticationException"></exception>
28+
Task ValidateAuthenticationAsync(IHeaderDictionary request);
2029
}
2130
}

ElevenLabs-DotNet-Proxy/Readme.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ In this example, we demonstrate how to set up and use `ElevenLabsProxyStartup` i
5151
- Powershell install: `Install-Package ElevenLabs-DotNet-Proxy`
5252
- Manually editing .csproj: `<PackageReference Include="ElevenLabs-DotNet-Proxy" />`
5353
3. Create a new class that inherits from `AbstractAuthenticationFilter` and override the `ValidateAuthentication` method. This will implement the `IAuthenticationFilter` that you will use to check user session token against your internal server.
54-
4. In `Program.cs`, create a new proxy web application by calling `ElevenLabsProxyStartup.CreateDefaultHost` method, passing your custom `AuthenticationFilter` as a type argument.
54+
4. In `Program.cs`, create a new proxy web application by calling `ElevenLabsProxyStartup.CreateWebApplication` method, passing your custom `AuthenticationFilter` as a type argument.
5555
5. Create `ElevenLabsAuthentication` and `ElevenLabsClientSettings` as you would normally with your API keys, org id, or Azure settings.
5656

5757
```csharp
@@ -63,7 +63,19 @@ public partial class Program
6363
{
6464
// You will need to implement your own class to properly test
6565
// custom issued tokens you've setup for your end users.
66-
if (!request["xi-api-key"].ToString().Contains(userToken))
66+
if (!request["xi-api-key"].ToString().Contains(TestUserToken))
67+
{
68+
throw new AuthenticationException("User is not authorized");
69+
}
70+
}
71+
72+
public override async Task ValidateAuthenticationAsync(IHeaderDictionary request)
73+
{
74+
await Task.CompletedTask; // remote resource call
75+
76+
// You will need to implement your own class to properly test
77+
// custom issued tokens you've setup for your end users.
78+
if (!request["xi-api-key"].ToString().Contains(TestUserToken))
6779
{
6880
throw new AuthenticationException("User is not authorized");
6981
}
@@ -72,9 +84,9 @@ public partial class Program
7284

7385
public static void Main(string[] args)
7486
{
75-
var client = new ElevenLabsClient();
76-
var proxy = ElevenLabsProxyStartup.CreateDefaultHost<AuthenticationFilter>(args, client);
77-
proxy.Run();
87+
var auth = ElevenLabsAuthentication.LoadFromEnv();
88+
var client = new ElevenLabsClient(auth);
89+
ElevenLabsProxyStartup.CreateWebApplication<AuthenticationFilter>(args, client).Run();
7890
}
7991
}
8092
```

ElevenLabs-DotNet-Tests-Proxy/Program.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
using ElevenLabs.Proxy;
44
using Microsoft.AspNetCore.Http;
5-
using Microsoft.Extensions.Hosting;
65
using System.Security.Authentication;
6+
using System.Threading.Tasks;
77

88
namespace ElevenLabs.Tests.Proxy
99
{
@@ -27,14 +27,25 @@ public override void ValidateAuthentication(IHeaderDictionary request)
2727
throw new AuthenticationException("User is not authorized");
2828
}
2929
}
30+
31+
public override async Task ValidateAuthenticationAsync(IHeaderDictionary request)
32+
{
33+
await Task.CompletedTask; // remote resource call
34+
35+
// You will need to implement your own class to properly test
36+
// custom issued tokens you've setup for your end users.
37+
if (!request["xi-api-key"].ToString().Contains(TestUserToken))
38+
{
39+
throw new AuthenticationException("User is not authorized");
40+
}
41+
}
3042
}
3143

3244
public static void Main(string[] args)
3345
{
3446
var auth = ElevenLabsAuthentication.LoadFromEnv();
3547
var client = new ElevenLabsClient(auth);
36-
var proxy = ElevenLabsProxyStartup.CreateDefaultHost<AuthenticationFilter>(args, client);
37-
proxy.Run();
48+
ElevenLabsProxyStartup.CreateWebApplication<AuthenticationFilter>(args, client).Run();
3849
}
3950
}
4051
}

0 commit comments

Comments
 (0)