Skip to content

Commit 50e97cb

Browse files
author
sjuarez
committed
Revert changes in Azure project introduced for Issue200751
1 parent 6b905ed commit 50e97cb

File tree

5 files changed

+573
-9
lines changed

5 files changed

+573
-9
lines changed

dotnet/src/extensions/Azure/Handlers/GeneXus.Deploy.AzureFunctions.Handlers.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
</ItemGroup>
5959

6060
<ItemGroup Condition="'$(HttpSupport)' == 'true'">
61-
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
61+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
6262
<Compile Include="HttpHandler\*" />
6363
</ItemGroup>
6464

@@ -69,7 +69,6 @@
6969

7070
<ItemGroup>
7171
<PackageReference Include="Azure.Core" Version="1.42.0" />
72-
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
7372
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.4" OutputItemType="Analyzer" />
7473
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.23.0" />
7574
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.IO;
6+
using System.IO.Pipelines;
7+
using System.Linq;
8+
using System.Security.Claims;
9+
using System.Text.Json.Nodes;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using GeneXus.Cache;
13+
using GeneXus.Utils;
14+
using Microsoft.AspNetCore.Http;
15+
using Microsoft.AspNetCore.Http.Features;
16+
using Microsoft.Azure.Functions.Worker.Http;
17+
using Microsoft.Extensions.Primitives;
18+
19+
namespace GeneXus.Deploy.AzureFunctions.HttpHandler
20+
{
21+
public class GXHttpAzureContextAccessor : HttpContext
22+
{
23+
DefaultHttpContext defaultHttpContext = new DefaultHttpContext();
24+
public HttpResponse httpResponseData;
25+
private ICacheService2 _redis;
26+
private string sessionId;
27+
private static readonly IGXLogger log = GXLoggerFactory.GetLogger<GXHttpAzureContextAccessor>();
28+
internal const string AzureSessionId = "GX_AZURE_SESSIONID";
29+
public GXHttpAzureContextAccessor(HttpRequestData requestData, HttpResponseData responseData, ICacheService2 redis)
30+
{
31+
if (redis != null)
32+
_redis = redis;
33+
34+
bool isSecure = false;
35+
foreach (var header in requestData.Headers)
36+
{
37+
string[] values = new Microsoft.Extensions.Primitives.StringValues(header.Value.Select(val => val).ToArray());
38+
defaultHttpContext.Request.Headers[header.Key] = new Microsoft.Extensions.Primitives.StringValues(values);
39+
40+
if (header.Key == "Cookie")
41+
{
42+
sessionId = CookieValue(defaultHttpContext.Request.Headers[header.Key], AzureSessionId);
43+
}
44+
45+
if (!isSecure)
46+
isSecure = GetSecureConnection(header.Key, defaultHttpContext.Request.Headers[header.Key]);
47+
48+
}
49+
if (requestData.FunctionContext.BindingContext != null)
50+
{
51+
IReadOnlyDictionary<string, object> keyValuePairs = requestData.FunctionContext.BindingContext.BindingData;
52+
object queryparamsJson = requestData.FunctionContext.BindingContext.BindingData.GetValueOrDefault("Query");
53+
JsonNode queryparams = JsonNode.Parse((string)queryparamsJson);
54+
55+
foreach (var keyValuePair in keyValuePairs)
56+
{
57+
if ((keyValuePair.Key != "Headers") && (keyValuePair.Key != "Query"))
58+
{
59+
JsonNode qKey = queryparams[keyValuePair.Key];
60+
if (qKey == null)
61+
defaultHttpContext.Request.RouteValues.Add(keyValuePair.Key.ToLower(), keyValuePair.Value);
62+
}
63+
}
64+
}
65+
66+
defaultHttpContext.Request.Method = requestData.Method;
67+
defaultHttpContext.Request.Body = requestData.Body;
68+
defaultHttpContext.Request.Path = PathString.FromUriComponent(requestData.Url);
69+
defaultHttpContext.Request.QueryString = QueryString.FromUriComponent(requestData.Url);
70+
71+
72+
IHttpRequestFeature requestFeature = defaultHttpContext.Features.Get<IHttpRequestFeature>();
73+
requestFeature.RawTarget = defaultHttpContext.Request.Path.HasValue ? defaultHttpContext.Request.Path.Value : String.Empty;
74+
defaultHttpContext.Features.Set<IHttpRequestFeature>(requestFeature);
75+
76+
if (string.IsNullOrEmpty(sessionId))
77+
{
78+
CreateSessionId(isSecure, responseData, requestData);
79+
}
80+
else //Refresh the session timestamp
81+
{
82+
if (Session is RedisHttpSession)
83+
{
84+
RedisHttpSession redisHttpSession = (RedisHttpSession)Session;
85+
//Check if session is in cache
86+
if (redisHttpSession.SessionKeyExists(sessionId))
87+
{
88+
bool success = redisHttpSession.RefreshSession(sessionId);
89+
if (!success)
90+
GXLogging.Debug(log, $"Azure Serverless: Session could not be refreshed :{sessionId}");
91+
}
92+
}
93+
}
94+
95+
httpResponseData = new GxHttpAzureResponse(defaultHttpContext, responseData);
96+
}
97+
private bool GetSecureConnection(string headerKey, string headerValue)
98+
{
99+
if ((headerKey == "Front-End-Https") & (headerValue == "on"))
100+
return true;
101+
102+
if ((headerKey == "X-Forwarded-Proto") & (headerValue == "https"))
103+
return true;
104+
105+
return false;
106+
}
107+
private void CreateSessionId(bool isSecure, HttpResponseData responseData, HttpRequestData requestData)
108+
{
109+
sessionId = Guid.NewGuid().ToString();
110+
HttpCookie sessionCookie = new HttpCookie(AzureSessionId, sessionId);
111+
112+
if (!isSecure)
113+
isSecure = requestData.Url.Scheme == "https";
114+
115+
if (!DateTime.MinValue.Equals(DateTimeUtil.NullDate()))
116+
sessionCookie.Expires = DateTime.MinValue;
117+
sessionCookie.Path = "";
118+
sessionCookie.Domain = "";
119+
sessionCookie.HttpOnly = true;
120+
sessionCookie.Secure = isSecure;
121+
122+
if (responseData.Cookies != null)
123+
responseData.Cookies.Append(sessionCookie);
124+
GXLogging.Debug(log, $"Create new Azure Session Id :{sessionId}");
125+
}
126+
private string CookieValue(string header, string name)
127+
{
128+
string[] words = header.Split(';');
129+
130+
foreach (string word in words)
131+
{
132+
string[] parts = word.Split('=');
133+
if (parts[0].Trim() == name)
134+
return parts[1];
135+
}
136+
return string.Empty;
137+
}
138+
public override IFeatureCollection Features => defaultHttpContext.Features;
139+
140+
public override HttpRequest Request => defaultHttpContext.Request;
141+
142+
public override HttpResponse Response => httpResponseData;
143+
144+
public override ConnectionInfo Connection => defaultHttpContext.Connection;
145+
146+
public override WebSocketManager WebSockets => defaultHttpContext.WebSockets;
147+
148+
public override ClaimsPrincipal User { get => defaultHttpContext.User; set => defaultHttpContext.User = value; }
149+
public override IDictionary<object, object> Items { get => defaultHttpContext.Items; set => defaultHttpContext.Items = value; }
150+
public override IServiceProvider RequestServices { get => defaultHttpContext.RequestServices; set => defaultHttpContext.RequestServices = value; }
151+
public override CancellationToken RequestAborted { get => defaultHttpContext.RequestAborted; set => defaultHttpContext.RequestAborted = value; }
152+
public override string TraceIdentifier { get => defaultHttpContext.TraceIdentifier; set => defaultHttpContext.TraceIdentifier = value; }
153+
public override ISession Session {
154+
155+
get
156+
{
157+
if ((_redis != null) & (sessionId != null))
158+
return new RedisHttpSession(_redis, sessionId);
159+
else return new MockHttpSession();
160+
}
161+
162+
set => defaultHttpContext.Session = value; }
163+
public override void Abort()
164+
{
165+
//throw new NotImplementedException();
166+
}
167+
}
168+
internal class GxAzureResponseHeaders : IHeaderDictionary
169+
{
170+
HeaderDictionary m_headers;
171+
HttpResponseData m_httpResponseData;
172+
internal GxAzureResponseHeaders(HttpResponseData httpResponseData)
173+
{
174+
m_headers = new HeaderDictionary();
175+
foreach (var header in httpResponseData.Headers)
176+
{
177+
string[] values = new Microsoft.Extensions.Primitives.StringValues(header.Value.Select(val => val).ToArray());
178+
m_headers.Add(header.Key, values);
179+
}
180+
m_httpResponseData = httpResponseData;
181+
}
182+
183+
public StringValues this[string key]
184+
{
185+
get
186+
{
187+
return m_headers[key];
188+
}
189+
set
190+
{
191+
m_httpResponseData.Headers.Add(key, value.AsEnumerable());
192+
m_headers[key] = value;
193+
}
194+
}
195+
196+
public long? ContentLength { get { return m_headers.ContentLength; } set {; } }
197+
198+
public ICollection<string> Keys { get { return m_headers.Keys; } }
199+
public ICollection<StringValues> Values { get { return m_headers.Values; } }
200+
201+
public int Count { get { return m_headers.Count; } }
202+
203+
public bool IsReadOnly { get { return m_headers.IsReadOnly; } }
204+
205+
public void Add(string key, StringValues value)
206+
{
207+
m_httpResponseData.Headers.Add(key, value.AsEnumerable());
208+
m_headers.Add(key, value);
209+
}
210+
211+
public void Add(KeyValuePair<string, StringValues> item)
212+
{
213+
m_httpResponseData.Headers.Add(item.Key, item.Value.AsEnumerable());
214+
m_headers.Add(item.Key, item.Value);
215+
}
216+
217+
public void Clear()
218+
{
219+
m_httpResponseData.Headers.Clear();
220+
m_headers.Clear();
221+
}
222+
223+
public bool Contains(KeyValuePair<string, StringValues> item)
224+
{
225+
return m_headers.Contains(item);
226+
}
227+
228+
public bool ContainsKey(string key)
229+
{
230+
return m_headers.ContainsKey(key);
231+
}
232+
233+
public void CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
234+
{
235+
m_headers.CopyTo(array, arrayIndex);
236+
}
237+
238+
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
239+
{
240+
return m_headers.GetEnumerator();
241+
}
242+
243+
public bool Remove(string key)
244+
{
245+
m_httpResponseData.Headers.Remove(key);
246+
return m_headers.Remove(key);
247+
}
248+
249+
public bool Remove(KeyValuePair<string, StringValues> item)
250+
{
251+
m_httpResponseData.Headers.Remove(item.Key);
252+
return m_headers.Remove(item);
253+
}
254+
255+
public bool TryGetValue(string key, [MaybeNullWhen(false)] out StringValues value)
256+
{
257+
return m_headers.TryGetValue(key, out value);
258+
}
259+
260+
IEnumerator IEnumerable.GetEnumerator()
261+
{
262+
return m_headers.GetEnumerator();
263+
}
264+
}
265+
266+
public class GxHttpAzureResponse : HttpResponse
267+
{
268+
HttpResponseData httpResponseData;
269+
HttpContext httpContext;
270+
271+
private FeatureReferences<FeatureInterfaces> _features;
272+
273+
private readonly static Func<IFeatureCollection, IHttpResponseFeature> _nullResponseFeature = f => null;
274+
private readonly static Func<IFeatureCollection, IHttpResponseBodyFeature> _nullResponseBodyFeature = f => null;
275+
private readonly static Func<IFeatureCollection, IResponseCookiesFeature> _newResponseCookiesFeature = f => new ResponseCookiesFeature(f);
276+
277+
struct FeatureInterfaces
278+
{
279+
public IHttpResponseFeature Response;
280+
public IHttpResponseBodyFeature ResponseBody;
281+
public IResponseCookiesFeature Cookies;
282+
}
283+
public void Initialize()
284+
{
285+
_features.Initalize(httpContext.Features);
286+
}
287+
public void Initialize(int revision)
288+
{
289+
_features.Initalize(httpContext.Features, revision);
290+
}
291+
292+
private IHttpResponseBodyFeature HttpResponseBodyFeature =>
293+
_features.Fetch(ref _features.Cache.ResponseBody, _nullResponseBodyFeature);
294+
295+
private IResponseCookiesFeature ResponseCookiesFeature =>
296+
_features.Fetch(ref _features.Cache.Cookies, _newResponseCookiesFeature);
297+
private IHttpResponseFeature HttpResponseFeature =>
298+
_features.Fetch(ref _features.Cache.Response, _nullResponseFeature);
299+
300+
public GxHttpAzureResponse(HttpContext context, HttpResponseData responseData)
301+
{
302+
httpResponseData = responseData;
303+
httpContext = context;
304+
_features.Initalize(context.Features);
305+
}
306+
public override HttpContext HttpContext => httpContext;
307+
308+
public override int StatusCode { get => (int)httpResponseData.StatusCode; set => httpResponseData.StatusCode = (System.Net.HttpStatusCode)value; }
309+
310+
public override IHeaderDictionary Headers
311+
{
312+
get
313+
{
314+
return new GxAzureResponseHeaders(httpResponseData);
315+
}
316+
}
317+
public override Stream Body { get => httpResponseData.Body; set => httpResponseData.Body = value; }
318+
public override long? ContentLength {get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
319+
public override string ContentType
320+
{
321+
get
322+
{
323+
var headers = from head in httpResponseData.Headers
324+
where head.Key == "Content-Type"
325+
select head;
326+
foreach (var header in headers)
327+
{
328+
string[] values = new Microsoft.Extensions.Primitives.StringValues(header.Value.Select(val => val).ToArray());
329+
return (values.First());
330+
}
331+
return ("application/json");
332+
}
333+
334+
set
335+
{
336+
if (!string.IsNullOrEmpty(ContentType))
337+
httpResponseData.Headers.Remove("Content-Type");
338+
httpResponseData.Headers.Add("Content-Type", value);
339+
}
340+
}
341+
public override IResponseCookies Cookies
342+
{
343+
get { return ResponseCookiesFeature.Cookies; }
344+
345+
}
346+
347+
public override bool HasStarted
348+
{
349+
get { return HttpResponseFeature.HasStarted; }
350+
}
351+
352+
public override void OnCompleted(Func<object, Task> callback, object state)
353+
{
354+
//throw new NotImplementedException();
355+
}
356+
public override void OnStarting(Func<object, Task> callback, object state)
357+
{
358+
//throw new NotImplementedException();
359+
}
360+
361+
public override void Redirect(string location, bool permanent)
362+
{
363+
//throw new NotImplementedException();
364+
}
365+
public override PipeWriter BodyWriter
366+
{
367+
get
368+
{
369+
return (PipeWriter.Create(Body));
370+
}
371+
}
372+
}
373+
}

0 commit comments

Comments
 (0)