diff --git a/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj b/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
index 44aba86..87f53ab 100644
--- a/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
+++ b/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
@@ -1,48 +1,48 @@
-
- net6.0
- disable
- disable
- True
- false
- false
- Stephen Hodgson
- ElevenLabs-DotNet-Proxy
- A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.
- 2023
- https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
- https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
- ElevenLabs, AI, ML, API, api-proxy, proxy, gateway
- ElevenLabs API Proxy
- ElevenLabs-DotNet-Proxy
- 1.0.1
- ElevenLabs.Proxy
- Initial Release!
- True
- false
- Readme.md
- True
- ElevenLabsIcon.png
- LICENSE
-
-
-
-
-
-
-
- True
- \
-
-
- True
- \
-
-
-
-
- True
- \
-
-
+
+ net6.0
+ disable
+ disable
+ True
+ false
+ false
+ Stephen Hodgson
+ ElevenLabs-DotNet-Proxy
+ A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.
+ 2024
+ https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
+ https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
+ ElevenLabs, AI, ML, API, api-proxy, proxy, gateway
+ ElevenLabs API Proxy
+ ElevenLabs-DotNet-Proxy
+ 1.2.0
+ ElevenLabs.Proxy
+ Initial Release!
+ True
+ false
+ Readme.md
+ True
+ ElevenLabsIcon.png
+ LICENSE
+
+
+
+
+
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
+
+ True
+ \
+
+
diff --git a/ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs b/ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
index 6323cf3..37a4f12 100644
--- a/ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
+++ b/ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
@@ -1,5 +1,6 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace ElevenLabs.Proxy
@@ -9,5 +10,8 @@ public abstract class AbstractAuthenticationFilter : IAuthenticationFilter
{
///
public abstract void ValidateAuthentication(IHeaderDictionary request);
+
+ ///
+ public abstract Task ValidateAuthenticationAsync(IHeaderDictionary request);
}
}
diff --git a/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
index af4f5a7..12a14e4 100644
--- a/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
+++ b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
@@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Net.Http.Headers;
@@ -26,7 +27,7 @@ public class ElevenLabsProxyStartup
private IAuthenticationFilter authenticationFilter;
// Copied from https://github.com/microsoft/reverse-proxy/blob/51d797986b1fea03500a1ad173d13a1176fb5552/src/ReverseProxy/Forwarder/RequestUtilities.cs#L61-L83
- private static readonly HashSet ExcludedHeaders = new HashSet()
+ private static readonly HashSet excludedHeaders = new()
{
HeaderNames.Connection,
HeaderNames.TransferEncoding,
@@ -49,7 +50,12 @@ public class ElevenLabsProxyStartup
#endif
};
- public void ConfigureServices(IServiceCollection services) { }
+ ///
+ /// Configures the and services.
+ ///
+ ///
+ public void ConfigureServices(IServiceCollection services)
+ => SetupServices(services.BuildServiceProvider());
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
@@ -58,8 +64,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseDeveloperExceptionPage();
}
- elevenLabsClient = app.ApplicationServices.GetRequiredService();
- authenticationFilter = app.ApplicationServices.GetRequiredService();
+ SetupServices(app.ApplicationServices);
app.UseHttpsRedirection();
app.UseRouting();
@@ -77,25 +82,43 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
/// Startup args.
/// with configured and .
public static IHost CreateDefaultHost(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
- {
- return Host.CreateDefaultBuilder(args)
+ => Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
- webBuilder.ConfigureKestrel(options =>
- {
- options.AllowSynchronousIO = false;
- options.Limits.MinRequestBodyDataRate = null;
- options.Limits.MinResponseDataRate = null;
- options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
- options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
- });
+ webBuilder.ConfigureKestrel(ConfigureKestrel);
})
.ConfigureServices(services =>
{
services.AddSingleton(elevenLabsClient);
services.AddSingleton();
}).Build();
+
+ public static WebApplication CreateWebApplication(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ builder.WebHost.ConfigureKestrel(ConfigureKestrel);
+ builder.Services.AddSingleton(elevenLabsClient);
+ builder.Services.AddSingleton();
+ var app = builder.Build();
+ var startup = new ElevenLabsProxyStartup();
+ startup.Configure(app, app.Environment);
+ return app;
+ }
+
+ private static void ConfigureKestrel(KestrelServerOptions options)
+ {
+ options.AllowSynchronousIO = false;
+ options.Limits.MinRequestBodyDataRate = null;
+ options.Limits.MinResponseDataRate = null;
+ options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
+ options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
+ }
+
+ private void SetupServices(IServiceProvider serviceProvider)
+ {
+ elevenLabsClient = serviceProvider.GetRequiredService();
+ authenticationFilter = serviceProvider.GetRequiredService();
}
private static async Task HealthEndpoint(HttpContext context)
@@ -115,31 +138,34 @@ private async Task HandleRequest(HttpContext httpContext, string endpoint)
{
try
{
+ // ReSharper disable once MethodHasAsyncOverload
+ // just in case either method is implemented we call it twice.
authenticationFilter.ValidateAuthentication(httpContext.Request.Headers);
+ await authenticationFilter.ValidateAuthenticationAsync(httpContext.Request.Headers);
var method = new HttpMethod(httpContext.Request.Method);
var uri = new Uri(string.Format(elevenLabsClient.ElevenLabsClientSettings.BaseRequestUrlFormat, $"{endpoint}{httpContext.Request.QueryString}"));
- var elevenLabsRequest = new HttpRequestMessage(method, uri);
+ using var request = new HttpRequestMessage(method, uri);
- elevenLabsRequest.Content = new StreamContent(httpContext.Request.Body);
+ request.Content = new StreamContent(httpContext.Request.Body);
if (httpContext.Request.ContentType != null)
{
- elevenLabsRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType);
+ request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType);
}
- var proxyResponse = await elevenLabsClient.Client.SendAsync(elevenLabsRequest, HttpCompletionOption.ResponseHeadersRead);
+ var proxyResponse = await elevenLabsClient.Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
httpContext.Response.StatusCode = (int)proxyResponse.StatusCode;
foreach (var (key, value) in proxyResponse.Headers)
{
- if (ExcludedHeaders.Contains(key)) { continue; }
+ if (excludedHeaders.Contains(key)) { continue; }
httpContext.Response.Headers[key] = value.ToArray();
}
foreach (var (key, value) in proxyResponse.Content.Headers)
{
- if (ExcludedHeaders.Contains(key)) { continue; }
+ if (excludedHeaders.Contains(key)) { continue; }
httpContext.Response.Headers[key] = value.ToArray();
}
diff --git a/ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs b/ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs
index d568b07..2b692e8 100644
--- a/ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs
+++ b/ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs
@@ -1,7 +1,8 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.
-using System.Security.Authentication;
using Microsoft.AspNetCore.Http;
+using System.Security.Authentication;
+using System.Threading.Tasks;
namespace ElevenLabs.Proxy
{
@@ -17,5 +18,13 @@ public interface IAuthenticationFilter
///
///
void ValidateAuthentication(IHeaderDictionary request);
+
+ ///
+ /// Checks the headers for your user issued token.
+ /// If it's not valid, then throw .
+ ///
+ ///
+ ///
+ Task ValidateAuthenticationAsync(IHeaderDictionary request);
}
}
diff --git a/ElevenLabs-DotNet/Authentication/ElevenLabsClientSettings.cs b/ElevenLabs-DotNet/Authentication/ElevenLabsClientSettings.cs
index 715e202..c876e39 100644
--- a/ElevenLabs-DotNet/Authentication/ElevenLabsClientSettings.cs
+++ b/ElevenLabs-DotNet/Authentication/ElevenLabsClientSettings.cs
@@ -6,8 +6,9 @@ namespace ElevenLabs
{
public sealed class ElevenLabsClientSettings
{
- internal const string ElevenLabsDomain = "api.elevenlabs.io";
+ internal const string Https = "https://";
internal const string DefaultApiVersion = "v1";
+ internal const string ElevenLabsDomain = "api.elevenlabs.io";
///
/// Creates a new instance of for use with ElevenLabs API.
@@ -17,7 +18,7 @@ public ElevenLabsClientSettings()
Domain = ElevenLabsDomain;
ApiVersion = "v1";
BaseRequest = $"/{ApiVersion}/";
- BaseRequestUrlFormat = $"https://{Domain}{BaseRequest}{{0}}";
+ BaseRequestUrlFormat = $"{Https}{Domain}{BaseRequest}{{0}}";
}
///
@@ -32,8 +33,8 @@ public ElevenLabsClientSettings(string domain, string apiVersion = DefaultApiVer
domain = ElevenLabsDomain;
}
- if (!domain.Contains(".") &&
- !domain.Contains(":"))
+ if (!domain.Contains('.') &&
+ !domain.Contains(':'))
{
throw new ArgumentException($"You're attempting to pass a \"resourceName\" parameter to \"{nameof(domain)}\". Please specify \"resourceName:\" for this parameter in constructor.");
}
@@ -43,10 +44,10 @@ public ElevenLabsClientSettings(string domain, string apiVersion = DefaultApiVer
apiVersion = DefaultApiVersion;
}
- Domain = domain;
+ Domain = domain.Contains("http") ? domain : $"{Https}{domain}";
ApiVersion = apiVersion;
BaseRequest = $"/{ApiVersion}/";
- BaseRequestUrlFormat = $"https://{Domain}{BaseRequest}{{0}}";
+ BaseRequestUrlFormat = $"{Domain}{BaseRequest}{{0}}";
}
public string Domain { get; }
@@ -57,6 +58,6 @@ public ElevenLabsClientSettings(string domain, string apiVersion = DefaultApiVer
public string BaseRequestUrlFormat { get; }
- public static ElevenLabsClientSettings Default { get; } = new ElevenLabsClientSettings();
+ public static ElevenLabsClientSettings Default { get; } = new();
}
}
diff --git a/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj b/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
index 831b40f..d7a2c84 100644
--- a/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
+++ b/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
@@ -12,10 +12,14 @@
Stephen Hodgson
ElevenLabs-DotNet
RageAgainstThePixel
- 2023
+ 2024
ElevenLabs, AI, ML, Voice, TTS
- 2.1.1
- Version 2.1.1
+ 2.2.0
+ Version 2.2.0
+- 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
+Version 2.1.1
- Added VoicesEndpoint.GetAllVoicesAsync overload that allows skipping downloading the voice settings
Version 2.1.0
- Added ElevenLabsClient.EnableDebug option to enable and disable for all endpoints
diff --git a/ElevenLabs-DotNet/ElevenLabsClient.cs b/ElevenLabs-DotNet/ElevenLabsClient.cs
index aa33a62..a064398 100644
--- a/ElevenLabs-DotNet/ElevenLabsClient.cs
+++ b/ElevenLabs-DotNet/ElevenLabsClient.cs
@@ -6,6 +6,7 @@
using ElevenLabs.User;
using ElevenLabs.VoiceGeneration;
using ElevenLabs.Voices;
+using System;
using System.Net.Http;
using System.Security.Authentication;
using System.Text.Json;
@@ -13,7 +14,7 @@
namespace ElevenLabs
{
- public sealed class ElevenLabsClient
+ public sealed class ElevenLabsClient : IDisposable
{
///
/// Creates a new client for the Eleven Labs API, handling auth and allowing for access to various API endpoints.
@@ -27,6 +28,12 @@ public sealed class ElevenLabsClient
///
/// Optional, .
/// Raised when authentication details are missing or invalid.
+ /// implements to manage the lifecycle of the resources it uses, including .
+ ///
+ /// When you initialize , it will create an internal instance if one is not provided.
+ /// This internal HttpClient is disposed of when ElevenLabsClient is disposed of.
+ /// If you provide an external HttpClient instance to ElevenLabsClient, you are responsible for managing its disposal.
+ ///
public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null, ElevenLabsClientSettings clientSettings = null, HttpClient httpClient = null)
{
ElevenLabsAuthentication = elevenLabsAuthentication ?? ElevenLabsAuthentication.Default;
@@ -37,7 +44,19 @@ public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null
throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/RageAgainstThePixel/ElevenLabs-DotNet#authentication for details.");
}
- Client = httpClient ?? new HttpClient();
+ if (httpClient == null)
+ {
+ httpClient = new HttpClient(new SocketsHttpHandler
+ {
+ PooledConnectionLifetime = TimeSpan.FromMinutes(15)
+ });
+ }
+ else
+ {
+ isCustomClient = true;
+ }
+
+ Client = httpClient;
Client.DefaultRequestHeaders.Add("User-Agent", "ElevenLabs-DotNet");
Client.DefaultRequestHeaders.Add("xi-api-key", ElevenLabsAuthentication.ApiKey);
@@ -49,6 +68,38 @@ public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null
VoiceGenerationEndpoint = new VoiceGenerationEndpoint(this);
}
+ ~ElevenLabsClient()
+ {
+ Dispose(false);
+ }
+
+ #region IDisposable
+
+ private bool isDisposed;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!isDisposed && disposing)
+ {
+ if (!isCustomClient)
+ {
+ Client?.Dispose();
+ }
+
+ isDisposed = true;
+ }
+ }
+
+ #endregion IDisposable
+
+ private bool isCustomClient;
+
///
/// to use when making calls to the API.
///
@@ -57,7 +108,7 @@ public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null
///
/// The to use when making calls to the API.
///
- internal static JsonSerializerOptions JsonSerializationOptions { get; } = new JsonSerializerOptions
+ internal static JsonSerializerOptions JsonSerializationOptions { get; } = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};