6
6
using Microsoft . AspNetCore . Server . Kestrel . Core ;
7
7
using Microsoft . Extensions . DependencyInjection ;
8
8
using Microsoft . Extensions . Hosting ;
9
- using Microsoft . Net . Http . Headers ;
10
9
using System ;
11
- using System . Collections . Generic ;
12
- using System . IO ;
13
- using System . Linq ;
14
- using System . Net . Http ;
15
- using System . Security . Authentication ;
16
10
using System . Threading . Tasks ;
17
- using MediaTypeHeaderValue = System . Net . Http . Headers . MediaTypeHeaderValue ;
18
11
19
12
namespace ElevenLabs . Proxy
20
13
{
@@ -26,37 +19,18 @@ public class ElevenLabsProxyStartup
26
19
private ElevenLabsClient elevenLabsClient ;
27
20
private IAuthenticationFilter authenticationFilter ;
28
21
29
- // Copied from https://github.com/microsoft/reverse-proxy/blob/51d797986b1fea03500a1ad173d13a1176fb5552/src/ReverseProxy/Forwarder/RequestUtilities.cs#L61-L83
30
- private static readonly HashSet < string > excludedHeaders = new ( )
31
- {
32
- HeaderNames . Connection ,
33
- HeaderNames . TransferEncoding ,
34
- HeaderNames . KeepAlive ,
35
- HeaderNames . Upgrade ,
36
- "Proxy-Connection" ,
37
- "Proxy-Authenticate" ,
38
- "Proxy-Authentication-Info" ,
39
- "Proxy-Authorization" ,
40
- "Proxy-Features" ,
41
- "Proxy-Instruction" ,
42
- "Security-Scheme" ,
43
- "ALPN" ,
44
- "Close" ,
45
- HeaderNames . TE ,
46
- #if NET
47
- HeaderNames . AltSvc ,
48
- #else
49
- "Alt-Svc" ,
50
- #endif
51
- } ;
52
-
53
22
/// <summary>
54
- /// Configures the <see cref="elevenLabsClient "/> and <see cref="IAuthenticationFilter"/> services.
23
+ /// Configures the <see cref="ElevenLabsClient "/> and <see cref="IAuthenticationFilter"/> services.
55
24
/// </summary>
56
25
/// <param name="services"></param>
57
26
public void ConfigureServices ( IServiceCollection services )
58
27
=> SetupServices ( services . BuildServiceProvider ( ) ) ;
59
28
29
+ /// <summary>
30
+ /// Configures the <see cref="IApplicationBuilder"/> to handle requests and forward them to OpenAI API.
31
+ /// </summary>
32
+ /// <param name="app"><see cref="IApplicationBuilder"/>.</param>
33
+ /// <param name="env"><see cref="IWebHostEnvironment"/>.</param>
60
34
public void Configure ( IApplicationBuilder app , IWebHostEnvironment env )
61
35
{
62
36
if ( env . IsDevelopment ( ) )
@@ -71,7 +45,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
71
45
app . UseEndpoints ( endpoints =>
72
46
{
73
47
endpoints . MapGet ( "/health" , HealthEndpoint ) ;
74
- endpoints . Map ( $ " { elevenLabsClient . ElevenLabsClientSettings . BaseRequest } {{**endpoint}}" , HandleRequest ) ;
48
+ endpoints . MapElevenLabsEndpoints ( elevenLabsClient , authenticationFilter ) ;
75
49
} ) ;
76
50
}
77
51
@@ -94,6 +68,12 @@ public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenL
94
68
services . AddSingleton < IAuthenticationFilter , T > ( ) ;
95
69
} ) . Build ( ) ;
96
70
71
+ /// <summary>
72
+ /// Creates a new <see cref="WebApplication"/> that acts as a proxy web api for OpenAI.
73
+ /// </summary>
74
+ /// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam>
75
+ /// <param name="args">Startup args.</param>
76
+ /// <param name="openAIClient"><see cref="OpenAIClient"/> with configured <see cref="OpenAIAuthentication"/> and <see cref="OpenAIClientSettings"/>.</param>
97
77
public static WebApplication CreateWebApplication < T > ( string [ ] args , ElevenLabsClient elevenLabsClient ) where T : class , IAuthenticationFilter
98
78
{
99
79
var builder = WebApplication . CreateBuilder ( args ) ;
@@ -130,75 +110,5 @@ private static async Task HealthEndpoint(HttpContext context)
130
110
const string content = "OK" ;
131
111
await context . Response . WriteAsync ( content ) ;
132
112
}
133
-
134
- /// <summary>
135
- /// Handles incoming requests, validates authentication, and forwards the request to ElevenLabs API
136
- /// </summary>
137
- private async Task HandleRequest ( HttpContext httpContext , string endpoint )
138
- {
139
- try
140
- {
141
- // ReSharper disable once MethodHasAsyncOverload
142
- // just in case either method is implemented we call it twice.
143
- authenticationFilter . ValidateAuthentication ( httpContext . Request . Headers ) ;
144
- await authenticationFilter . ValidateAuthenticationAsync ( httpContext . Request . Headers ) ;
145
-
146
- var method = new HttpMethod ( httpContext . Request . Method ) ;
147
- var uri = new Uri ( string . Format ( elevenLabsClient . ElevenLabsClientSettings . BaseRequestUrlFormat , $ "{ endpoint } { httpContext . Request . QueryString } ") ) ;
148
- using var request = new HttpRequestMessage ( method , uri ) ;
149
-
150
- request . Content = new StreamContent ( httpContext . Request . Body ) ;
151
-
152
- if ( httpContext . Request . ContentType != null )
153
- {
154
- request . Content . Headers . ContentType = MediaTypeHeaderValue . Parse ( httpContext . Request . ContentType ) ;
155
- }
156
-
157
- var proxyResponse = await elevenLabsClient . Client . SendAsync ( request , HttpCompletionOption . ResponseHeadersRead ) ;
158
- httpContext . Response . StatusCode = ( int ) proxyResponse . StatusCode ;
159
-
160
- foreach ( var ( key , value ) in proxyResponse . Headers )
161
- {
162
- if ( excludedHeaders . Contains ( key ) ) { continue ; }
163
- httpContext . Response . Headers [ key ] = value . ToArray ( ) ;
164
- }
165
-
166
- foreach ( var ( key , value ) in proxyResponse . Content . Headers )
167
- {
168
- if ( excludedHeaders . Contains ( key ) ) { continue ; }
169
- httpContext . Response . Headers [ key ] = value . ToArray ( ) ;
170
- }
171
-
172
- httpContext . Response . ContentType = proxyResponse . Content . Headers . ContentType ? . ToString ( ) ?? string . Empty ;
173
- const string streamingContent = "text/event-stream" ;
174
-
175
- if ( httpContext . Response . ContentType . Equals ( streamingContent ) )
176
- {
177
- var stream = await proxyResponse . Content . ReadAsStreamAsync ( ) ;
178
- await WriteServerStreamEventsAsync ( httpContext , stream ) ;
179
- }
180
- else
181
- {
182
- await proxyResponse . Content . CopyToAsync ( httpContext . Response . Body ) ;
183
- }
184
- }
185
- catch ( AuthenticationException authenticationException )
186
- {
187
- httpContext . Response . StatusCode = StatusCodes . Status401Unauthorized ;
188
- await httpContext . Response . WriteAsync ( authenticationException . Message ) ;
189
- }
190
- catch ( Exception e )
191
- {
192
- httpContext . Response . StatusCode = StatusCodes . Status500InternalServerError ;
193
- await httpContext . Response . WriteAsync ( e . Message ) ;
194
- }
195
- }
196
-
197
- private static async Task WriteServerStreamEventsAsync ( HttpContext httpContext , Stream contentStream )
198
- {
199
- var responseStream = httpContext . Response . Body ;
200
- await contentStream . CopyToAsync ( responseStream , httpContext . RequestAborted ) ;
201
- await responseStream . FlushAsync ( httpContext . RequestAborted ) ;
202
- }
203
113
}
204
114
}
0 commit comments