Skip to content

Commit

Permalink
Merge branch 'development' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenHodgson authored May 6, 2024
2 parents 6c52bf7 + c5c00d0 commit e64cf84
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 80 deletions.
92 changes: 46 additions & 46 deletions ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<Authors>Stephen Hodgson</Authors>
<Product>ElevenLabs-DotNet-Proxy</Product>
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description>
<Copyright>2023</Copyright>
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl>
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl>
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags>
<Title>ElevenLabs API Proxy</Title>
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
<Version>1.0.1</Version>
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
<PackageReleaseNotes>Initial Release!</PackageReleaseNotes>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageReadmeFile>Readme.md</PackageReadmeFile>
<IncludeSymbols>True</IncludeSymbols>
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Update="Readme.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<Authors>Stephen Hodgson</Authors>
<Product>ElevenLabs-DotNet-Proxy</Product>
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description>
<Copyright>2024</Copyright>
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl>
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl>
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags>
<Title>ElevenLabs API Proxy</Title>
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
<Version>1.2.0</Version>
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
<PackageReleaseNotes>Initial Release!</PackageReleaseNotes>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageReadmeFile>Readme.md</PackageReadmeFile>
<IncludeSymbols>True</IncludeSymbols>
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Update="Readme.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,5 +10,8 @@ public abstract class AbstractAuthenticationFilter : IAuthenticationFilter
{
/// <inheritdoc />
public abstract void ValidateAuthentication(IHeaderDictionary request);

/// <inheritdoc />
public abstract Task ValidateAuthenticationAsync(IHeaderDictionary request);
}
}
66 changes: 46 additions & 20 deletions ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<string> ExcludedHeaders = new HashSet<string>()
private static readonly HashSet<string> excludedHeaders = new()
{
HeaderNames.Connection,
HeaderNames.TransferEncoding,
Expand All @@ -49,7 +50,12 @@ public class ElevenLabsProxyStartup
#endif
};

public void ConfigureServices(IServiceCollection services) { }
/// <summary>
/// Configures the <see cref="elevenLabsClient"/> and <see cref="IAuthenticationFilter"/> services.
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
=> SetupServices(services.BuildServiceProvider());

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Expand All @@ -58,8 +64,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseDeveloperExceptionPage();
}

elevenLabsClient = app.ApplicationServices.GetRequiredService<ElevenLabsClient>();
authenticationFilter = app.ApplicationServices.GetRequiredService<IAuthenticationFilter>();
SetupServices(app.ApplicationServices);

app.UseHttpsRedirection();
app.UseRouting();
Expand All @@ -77,25 +82,43 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
/// <param name="args">Startup args.</param>
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
{
return Host.CreateDefaultBuilder(args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<ElevenLabsProxyStartup>();
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<IAuthenticationFilter, T>();
}).Build();

public static WebApplication CreateWebApplication<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
{
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(ConfigureKestrel);
builder.Services.AddSingleton(elevenLabsClient);
builder.Services.AddSingleton<IAuthenticationFilter, T>();
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<ElevenLabsClient>();
authenticationFilter = serviceProvider.GetRequiredService<IAuthenticationFilter>();
}

private static async Task HealthEndpoint(HttpContext context)
Expand All @@ -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();
}

Expand Down
11 changes: 10 additions & 1 deletion ElevenLabs-DotNet-Proxy/Proxy/IAuthenticationFilter.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -17,5 +18,13 @@ public interface IAuthenticationFilter
/// <param name="request"></param>
/// <exception cref="AuthenticationException"></exception>
void ValidateAuthentication(IHeaderDictionary request);

/// <summary>
/// Checks the headers for your user issued token.
/// If it's not valid, then throw <see cref="AuthenticationException"/>.
/// </summary>
/// <param name="request"></param>
/// <exception cref="AuthenticationException"></exception>
Task ValidateAuthenticationAsync(IHeaderDictionary request);
}
}
15 changes: 8 additions & 7 deletions ElevenLabs-DotNet/Authentication/ElevenLabsClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/// <summary>
/// Creates a new instance of <see cref="ElevenLabsClientSettings"/> for use with ElevenLabs API.
Expand All @@ -17,7 +18,7 @@ public ElevenLabsClientSettings()
Domain = ElevenLabsDomain;
ApiVersion = "v1";
BaseRequest = $"/{ApiVersion}/";
BaseRequestUrlFormat = $"https://{Domain}{BaseRequest}{{0}}";
BaseRequestUrlFormat = $"{Https}{Domain}{BaseRequest}{{0}}";
}

/// <summary>
Expand All @@ -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.");
}
Expand All @@ -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; }
Expand All @@ -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();
}
}
10 changes: 7 additions & 3 deletions ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
<Authors>Stephen Hodgson</Authors>
<Title>ElevenLabs-DotNet</Title>
<Company>RageAgainstThePixel</Company>
<Copyright>2023</Copyright>
<Copyright>2024</Copyright>
<PackageTags>ElevenLabs, AI, ML, Voice, TTS</PackageTags>
<Version>2.1.1</Version>
<PackageReleaseNotes>Version 2.1.1
<Version>2.2.0</Version>
<PackageReleaseNotes>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
Expand Down
Loading

0 comments on commit e64cf84

Please sign in to comment.