From 1517f7e77f61e1ba8dc45f18bedb4973ee2cd78a Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Sun, 4 Aug 2024 10:40:53 +1000 Subject: [PATCH 1/6] generalise query parameter processing --- src/AzureAIProxy/Routes/AzureAISearch.cs | 8 ++++---- src/AzureAIProxy/Routes/AzureOpenAI.cs | 8 ++++---- src/AzureAIProxy/Services/IProxyService.cs | 1 + src/AzureAIProxy/Services/MockProxyService.cs | 15 +++++++++++++-- src/AzureAIProxy/Services/ProxyService.cs | 15 +++++++++++++-- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/AzureAIProxy/Routes/AzureAISearch.cs b/src/AzureAIProxy/Routes/AzureAISearch.cs index c5c90cd1..c66287c4 100644 --- a/src/AzureAIProxy/Routes/AzureAISearch.cs +++ b/src/AzureAIProxy/Routes/AzureAISearch.cs @@ -19,7 +19,6 @@ public static RouteGroupBuilder MapAzureAISearchRoutes(this RouteGroupBuilder bu private static async Task ProcessRequestAsync( [FromServices] ICatalogService catalogService, [FromServices] IProxyService proxyService, - [FromQuery(Name = "api-version")] string apiVersion, [FromBody] JsonDocument requestJsonDoc, HttpContext context, string index @@ -46,10 +45,11 @@ string index $"Deployment '{index}' not found for this event. Available deployments are: {string.Join(", ", eventCatalog.Select(d => d.DeploymentName))}" ); - var url = GenerateEndpointUrl(deployment, extPath, apiVersion); + var url = GenerateEndpointUrl(deployment, extPath); var (responseContent, statusCode) = await proxyService.HttpPostAsync( url, deployment.EndpointKey, + context, requestJsonDoc, requestContext, deployment @@ -58,7 +58,7 @@ string index } } - private static Uri GenerateEndpointUrl(Deployment deployment, string extPath, string apiVersion) + private static Uri GenerateEndpointUrl(Deployment deployment, string extPath) { var baseUrl = deployment.EndpointUrl.TrimEnd('/'); @@ -70,6 +70,6 @@ private static Uri GenerateEndpointUrl(Deployment deployment, string extPath, st _ => throw new ArgumentException("Invalid route pattern"), }; - return new Uri($"{baseUrl}{path}?api-version={apiVersion}"); + return new Uri($"{baseUrl}{path}"); } } diff --git a/src/AzureAIProxy/Routes/AzureOpenAI.cs b/src/AzureAIProxy/Routes/AzureOpenAI.cs index e19c6542..e1a2ea77 100644 --- a/src/AzureAIProxy/Routes/AzureOpenAI.cs +++ b/src/AzureAIProxy/Routes/AzureOpenAI.cs @@ -24,7 +24,6 @@ public static RouteGroupBuilder MapAzureOpenAIRoutes(this RouteGroupBuilder buil private static async Task ProcessRequestAsync( [FromServices] ICatalogService catalogService, [FromServices] IProxyService proxyService, - [FromQuery(Name = "api-version")] string apiVersion, [FromBody] JsonDocument requestJsonDoc, HttpContext context, string deploymentName @@ -66,7 +65,7 @@ string deploymentName ); } - var url = GenerateEndpointUrl(deployment, extPath, apiVersion); + var url = GenerateEndpointUrl(deployment, extPath); try { @@ -87,6 +86,7 @@ await proxyService.HttpPostStreamAsync( var (responseContent, statusCode) = await proxyService.HttpPostAsync( url, deployment.EndpointKey, + context, requestJsonDoc, requestContext, deployment @@ -130,10 +130,10 @@ private static bool IsStreaming(JsonDocument requestJsonDoc) : null; } - private static Uri GenerateEndpointUrl(Deployment deployment, string extPath, string apiVersion) + private static Uri GenerateEndpointUrl(Deployment deployment, string extPath) { var baseUrl = $"{deployment.EndpointUrl.TrimEnd('/')}/openai/deployments/{deployment.DeploymentName.Trim()}"; - return new Uri($"{baseUrl}{extPath}?api-version={apiVersion}"); + return new Uri($"{baseUrl}{extPath}"); } } diff --git a/src/AzureAIProxy/Services/IProxyService.cs b/src/AzureAIProxy/Services/IProxyService.cs index e424785d..3a11c0ae 100644 --- a/src/AzureAIProxy/Services/IProxyService.cs +++ b/src/AzureAIProxy/Services/IProxyService.cs @@ -8,6 +8,7 @@ public interface IProxyService Task<(string responseContent, int statusCode)> HttpPostAsync( Uri requestUrl, string endpointKey, + HttpContext context, JsonDocument requestJsonDoc, RequestContext requestContext, Deployment deployment diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 1203ef6f..753d8a54 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -25,6 +25,7 @@ private static int RandomDelayInMilliseconds public async Task<(string responseContent, int statusCode)> HttpPostAsync( Uri requestUrl, string endpointKey, + HttpContext context, JsonDocument requestJsonDoc, RequestContext requestContext, Deployment deployment @@ -33,7 +34,8 @@ Deployment deployment using var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl); + string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), Encoding.UTF8, @@ -72,7 +74,8 @@ Deployment deployment var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl); + string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), Encoding.UTF8, @@ -121,4 +124,12 @@ bool streaming + "\"}"; } } + + private static string AppendQueryParameters(Uri requestUrl, HttpContext context) + { + var queryCollection = context.Request.Query; + var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); + var requestUrlWithQuery = $"{requestUrl}?{queryString}"; + return requestUrlWithQuery; + } } diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index a1d1850f..8e57c7a9 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -19,6 +19,7 @@ public class ProxyService(IHttpClientFactory httpClientFactory, IMetricService m public async Task<(string responseContent, int statusCode)> HttpPostAsync( Uri requestUrl, string endpointKey, + HttpContext context, JsonDocument requestJsonDoc, RequestContext requestContext, Deployment deployment @@ -27,7 +28,8 @@ Deployment deployment using var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl); + string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), Encoding.UTF8, @@ -62,7 +64,8 @@ Deployment deployment var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl); + string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), Encoding.UTF8, @@ -82,4 +85,12 @@ Deployment deployment await using var responseStream = await response.Content.ReadAsStreamAsync(); await responseStream.CopyToAsync(context.Response.Body); } + + private static string AppendQueryParameters(Uri requestUrl, HttpContext context) + { + var queryCollection = context.Request.Query; + var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); + var requestUrlWithQuery = $"{requestUrl}?{queryString}"; + return requestUrlWithQuery; + } } From 01903cd353e45632c20b0b3a99c19ad319b00661 Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Sun, 4 Aug 2024 16:49:21 +1000 Subject: [PATCH 2/6] optimise uri generation --- src/AzureAIProxy/Routes/AzureAISearch.cs | 4 ++-- src/AzureAIProxy/Routes/AzureOpenAI.cs | 4 ++-- src/AzureAIProxy/Services/IProxyService.cs | 4 ++-- src/AzureAIProxy/Services/MockProxyService.cs | 15 +++++++++------ src/AzureAIProxy/Services/ProxyService.cs | 15 +++++++++------ 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/AzureAIProxy/Routes/AzureAISearch.cs b/src/AzureAIProxy/Routes/AzureAISearch.cs index c66287c4..d36fc380 100644 --- a/src/AzureAIProxy/Routes/AzureAISearch.cs +++ b/src/AzureAIProxy/Routes/AzureAISearch.cs @@ -58,7 +58,7 @@ string index } } - private static Uri GenerateEndpointUrl(Deployment deployment, string extPath) + private static string GenerateEndpointUrl(Deployment deployment, string extPath) { var baseUrl = deployment.EndpointUrl.TrimEnd('/'); @@ -70,6 +70,6 @@ private static Uri GenerateEndpointUrl(Deployment deployment, string extPath) _ => throw new ArgumentException("Invalid route pattern"), }; - return new Uri($"{baseUrl}{path}"); + return $"{baseUrl}{path}"; } } diff --git a/src/AzureAIProxy/Routes/AzureOpenAI.cs b/src/AzureAIProxy/Routes/AzureOpenAI.cs index e1a2ea77..a8497e5a 100644 --- a/src/AzureAIProxy/Routes/AzureOpenAI.cs +++ b/src/AzureAIProxy/Routes/AzureOpenAI.cs @@ -130,10 +130,10 @@ private static bool IsStreaming(JsonDocument requestJsonDoc) : null; } - private static Uri GenerateEndpointUrl(Deployment deployment, string extPath) + private static string GenerateEndpointUrl(Deployment deployment, string extPath) { var baseUrl = $"{deployment.EndpointUrl.TrimEnd('/')}/openai/deployments/{deployment.DeploymentName.Trim()}"; - return new Uri($"{baseUrl}{extPath}"); + return $"{baseUrl}{extPath}"; } } diff --git a/src/AzureAIProxy/Services/IProxyService.cs b/src/AzureAIProxy/Services/IProxyService.cs index 3a11c0ae..3e58bc55 100644 --- a/src/AzureAIProxy/Services/IProxyService.cs +++ b/src/AzureAIProxy/Services/IProxyService.cs @@ -6,7 +6,7 @@ namespace AzureAIProxy.Services; public interface IProxyService { Task<(string responseContent, int statusCode)> HttpPostAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -14,7 +14,7 @@ public interface IProxyService Deployment deployment ); Task HttpPostStreamAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 753d8a54..8e191454 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -23,7 +23,7 @@ private static int RandomDelayInMilliseconds /// This method is used for testing purposes and does not actually send an HTTP request. /// public async Task<(string responseContent, int statusCode)> HttpPostAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -34,7 +34,7 @@ Deployment deployment using var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + var requestUrlWithQuery = AppendQueryParameters(requestUrl, context); using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), @@ -63,7 +63,7 @@ Deployment deployment /// This method is used for testing purposes and does not actually send an HTTP request. /// public async Task HttpPostStreamAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -74,7 +74,7 @@ Deployment deployment var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + var requestUrlWithQuery = AppendQueryParameters(requestUrl, context); using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), @@ -125,11 +125,14 @@ bool streaming } } - private static string AppendQueryParameters(Uri requestUrl, HttpContext context) + private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryCollection = context.Request.Query; + if (queryCollection.Count == 0) + return new Uri(requestUrl); + var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); var requestUrlWithQuery = $"{requestUrl}?{queryString}"; - return requestUrlWithQuery; + return new Uri(requestUrlWithQuery); } } diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index 8e57c7a9..ff21c098 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -17,7 +17,7 @@ public class ProxyService(IHttpClientFactory httpClientFactory, IMetricService m /// The endpoint key to use for authentication. /// A tuple containing the response content and the status code of the HTTP response. public async Task<(string responseContent, int statusCode)> HttpPostAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -28,7 +28,7 @@ Deployment deployment using var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + var requestUrlWithQuery = AppendQueryParameters(requestUrl, context); using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), @@ -53,7 +53,7 @@ Deployment deployment /// The request body as a string. /// A task representing the asynchronous operation. public async Task HttpPostStreamAsync( - Uri requestUrl, + string requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -64,7 +64,7 @@ Deployment deployment var httpClient = httpClientFactory.CreateClient(); httpClient.Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds); - string requestUrlWithQuery = AppendQueryParameters(requestUrl, context); + var requestUrlWithQuery = AppendQueryParameters(requestUrl, context); using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrlWithQuery); requestMessage.Content = new StringContent( requestJsonDoc.RootElement.ToString(), @@ -86,11 +86,14 @@ Deployment deployment await responseStream.CopyToAsync(context.Response.Body); } - private static string AppendQueryParameters(Uri requestUrl, HttpContext context) + private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryCollection = context.Request.Query; + if (queryCollection.Count == 0) + return new Uri(requestUrl); + var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); var requestUrlWithQuery = $"{requestUrl}?{queryString}"; - return requestUrlWithQuery; + return new Uri(requestUrlWithQuery); } } From d803a6b0b619479274ad99a0d437a1b0e285dbc4 Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Sun, 4 Aug 2024 17:19:10 +1000 Subject: [PATCH 3/6] docs --- src/AzureAIProxy/Services/MockProxyService.cs | 6 ++++++ src/AzureAIProxy/Services/ProxyService.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 8e191454..1eaf2d76 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -125,6 +125,12 @@ bool streaming } } + /// + /// Appends query parameters from the specified to the given request URL. + /// + /// The request URL to append the query parameters to. + /// The containing the query parameters. + /// A new object with the appended query parameters. private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryCollection = context.Request.Query; diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index ff21c098..a8ade190 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -86,6 +86,12 @@ Deployment deployment await responseStream.CopyToAsync(context.Response.Body); } + /// + /// Appends query parameters from the specified to the given request URL. + /// + /// The request URL to append the query parameters to. + /// The containing the query parameters. + /// A new object with the appended query parameters. private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryCollection = context.Request.Query; From 0dd2bee1ff60cfaa56f7dc7dc855ac91c2e6be74 Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Sun, 4 Aug 2024 17:41:08 +1000 Subject: [PATCH 4/6] optimised url generation --- src/AzureAIProxy/Services/MockProxyService.cs | 14 +++++++------- src/AzureAIProxy/Services/ProxyService.cs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 1eaf2d76..76ecca72 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -133,12 +133,12 @@ bool streaming /// A new object with the appended query parameters. private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { - var queryCollection = context.Request.Query; - if (queryCollection.Count == 0) - return new Uri(requestUrl); - - var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); - var requestUrlWithQuery = $"{requestUrl}?{queryString}"; - return new Uri(requestUrlWithQuery); + var queryParameters = context.Request.Query + .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); + var uriBuilder = new UriBuilder(requestUrl) + { + Query = string.Join("&", queryParameters) + }; + return uriBuilder.Uri; } } diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index a8ade190..10022f5d 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -94,12 +94,12 @@ Deployment deployment /// A new object with the appended query parameters. private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { - var queryCollection = context.Request.Query; - if (queryCollection.Count == 0) - return new Uri(requestUrl); - - var queryString = string.Join("&", queryCollection.Select(q => $"{q.Key}={q.Value}")); - var requestUrlWithQuery = $"{requestUrl}?{queryString}"; - return new Uri(requestUrlWithQuery); + var queryParameters = context.Request.Query + .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); + var uriBuilder = new UriBuilder(requestUrl) + { + Query = string.Join("&", queryParameters) + }; + return uriBuilder.Uri; } } From 7ea1fee11284b0a97411ea13fc6a707b568a0c34 Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Sun, 4 Aug 2024 17:51:49 +1000 Subject: [PATCH 5/6] optimise --- src/AzureAIProxy/Services/MockProxyService.cs | 1 + src/AzureAIProxy/Services/ProxyService.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 76ecca72..72e21f0f 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -134,6 +134,7 @@ bool streaming private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryParameters = context.Request.Query + .Where(q => !string.IsNullOrEmpty(q.Value)) // Skip parameters with empty values .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); var uriBuilder = new UriBuilder(requestUrl) { diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index 10022f5d..57917e9d 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -95,6 +95,7 @@ Deployment deployment private static Uri AppendQueryParameters(string requestUrl, HttpContext context) { var queryParameters = context.Request.Query + .Where(q => !string.IsNullOrEmpty(q.Value)) // Skip parameters with empty values .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); var uriBuilder = new UriBuilder(requestUrl) { From fab3350c57bca4f2549027a1ad1af60eac14fea8 Mon Sep 17 00:00:00 2001 From: Dave Glover Date: Mon, 5 Aug 2024 11:53:36 +1000 Subject: [PATCH 6/6] pass UriBuilder object --- src/AzureAIProxy/Routes/AzureAISearch.cs | 9 +++++---- src/AzureAIProxy/Routes/AzureOpenAI.cs | 9 +++++---- src/AzureAIProxy/Services/IProxyService.cs | 4 ++-- src/AzureAIProxy/Services/MockProxyService.cs | 16 +++++++--------- src/AzureAIProxy/Services/ProxyService.cs | 16 +++++++--------- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/AzureAIProxy/Routes/AzureAISearch.cs b/src/AzureAIProxy/Routes/AzureAISearch.cs index d36fc380..f4e13016 100644 --- a/src/AzureAIProxy/Routes/AzureAISearch.cs +++ b/src/AzureAIProxy/Routes/AzureAISearch.cs @@ -58,10 +58,8 @@ string index } } - private static string GenerateEndpointUrl(Deployment deployment, string extPath) + private static UriBuilder GenerateEndpointUrl(Deployment deployment, string extPath) { - var baseUrl = deployment.EndpointUrl.TrimEnd('/'); - string path = extPath switch { "/{index}/docs/search" => $"/indexes/{deployment.DeploymentName.Trim()}/docs/search", @@ -70,6 +68,9 @@ private static string GenerateEndpointUrl(Deployment deployment, string extPath) _ => throw new ArgumentException("Invalid route pattern"), }; - return $"{baseUrl}{path}"; + return new UriBuilder(deployment.EndpointUrl.TrimEnd('/')) + { + Path = path + }; } } diff --git a/src/AzureAIProxy/Routes/AzureOpenAI.cs b/src/AzureAIProxy/Routes/AzureOpenAI.cs index a8497e5a..35e2dee3 100644 --- a/src/AzureAIProxy/Routes/AzureOpenAI.cs +++ b/src/AzureAIProxy/Routes/AzureOpenAI.cs @@ -130,10 +130,11 @@ private static bool IsStreaming(JsonDocument requestJsonDoc) : null; } - private static string GenerateEndpointUrl(Deployment deployment, string extPath) + private static UriBuilder GenerateEndpointUrl(Deployment deployment, string extPath) { - var baseUrl = - $"{deployment.EndpointUrl.TrimEnd('/')}/openai/deployments/{deployment.DeploymentName.Trim()}"; - return $"{baseUrl}{extPath}"; + return new UriBuilder(deployment.EndpointUrl.TrimEnd('/')) + { + Path = $"/openai/deployments/{deployment.DeploymentName.Trim()}{extPath}" + }; } } diff --git a/src/AzureAIProxy/Services/IProxyService.cs b/src/AzureAIProxy/Services/IProxyService.cs index 3e58bc55..cfe3506b 100644 --- a/src/AzureAIProxy/Services/IProxyService.cs +++ b/src/AzureAIProxy/Services/IProxyService.cs @@ -6,7 +6,7 @@ namespace AzureAIProxy.Services; public interface IProxyService { Task<(string responseContent, int statusCode)> HttpPostAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -14,7 +14,7 @@ public interface IProxyService Deployment deployment ); Task HttpPostStreamAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, diff --git a/src/AzureAIProxy/Services/MockProxyService.cs b/src/AzureAIProxy/Services/MockProxyService.cs index 72e21f0f..0d6b8968 100644 --- a/src/AzureAIProxy/Services/MockProxyService.cs +++ b/src/AzureAIProxy/Services/MockProxyService.cs @@ -23,7 +23,7 @@ private static int RandomDelayInMilliseconds /// This method is used for testing purposes and does not actually send an HTTP request. /// public async Task<(string responseContent, int statusCode)> HttpPostAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -63,7 +63,7 @@ Deployment deployment /// This method is used for testing purposes and does not actually send an HTTP request. /// public async Task HttpPostStreamAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -131,15 +131,13 @@ bool streaming /// The request URL to append the query parameters to. /// The containing the query parameters. /// A new object with the appended query parameters. - private static Uri AppendQueryParameters(string requestUrl, HttpContext context) + private static Uri AppendQueryParameters(UriBuilder requestUrl, HttpContext context) { var queryParameters = context.Request.Query .Where(q => !string.IsNullOrEmpty(q.Value)) // Skip parameters with empty values - .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); - var uriBuilder = new UriBuilder(requestUrl) - { - Query = string.Join("&", queryParameters) - }; - return uriBuilder.Uri; + .Select(q => $"{q.Key}={q.Value!}"); + + requestUrl.Query = string.Join("&", queryParameters); + return requestUrl.Uri; } } diff --git a/src/AzureAIProxy/Services/ProxyService.cs b/src/AzureAIProxy/Services/ProxyService.cs index 57917e9d..1205572c 100644 --- a/src/AzureAIProxy/Services/ProxyService.cs +++ b/src/AzureAIProxy/Services/ProxyService.cs @@ -17,7 +17,7 @@ public class ProxyService(IHttpClientFactory httpClientFactory, IMetricService m /// The endpoint key to use for authentication. /// A tuple containing the response content and the status code of the HTTP response. public async Task<(string responseContent, int statusCode)> HttpPostAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -53,7 +53,7 @@ Deployment deployment /// The request body as a string. /// A task representing the asynchronous operation. public async Task HttpPostStreamAsync( - string requestUrl, + UriBuilder requestUrl, string endpointKey, HttpContext context, JsonDocument requestJsonDoc, @@ -92,15 +92,13 @@ Deployment deployment /// The request URL to append the query parameters to. /// The containing the query parameters. /// A new object with the appended query parameters. - private static Uri AppendQueryParameters(string requestUrl, HttpContext context) + private static Uri AppendQueryParameters(UriBuilder requestUrl, HttpContext context) { var queryParameters = context.Request.Query .Where(q => !string.IsNullOrEmpty(q.Value)) // Skip parameters with empty values - .Select(q => $"{Uri.EscapeDataString(q.Key)}={Uri.EscapeDataString(q.Value!)}"); - var uriBuilder = new UriBuilder(requestUrl) - { - Query = string.Join("&", queryParameters) - }; - return uriBuilder.Uri; + .Select(q => $"{q.Key}={q.Value!}"); + + requestUrl.Query = string.Join("&", queryParameters); + return requestUrl.Uri; } }