Skip to content

Commit 81ad726

Browse files
author
HackPoint
committed
feat: Middleware implementation
1 parent 7610af4 commit 81ad726

22 files changed

+267
-118
lines changed

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Middleware/ClientCookieMiddleware.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/ClientCookieMiddleware.cs

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,12 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
using System;
17-
using System.Collections.Concurrent;
1816
using System.Collections.Generic;
19-
using System.Globalization;
20-
using System.Net;
21-
using Apache.Arrow.Flight.Sql.Middleware.Extensions;
22-
using Apache.Arrow.Flight.Sql.Middleware.Interfaces;
23-
using Apache.Arrow.Flight.Sql.Middleware.Models;
17+
using Apache.Arrow.Flight.Middleware.Interfaces;
18+
using Apache.Arrow.Flight.Middleware.Models;
2419
using Microsoft.Extensions.Logging;
2520

26-
namespace Apache.Arrow.Flight.Sql.Middleware.Middleware;
21+
namespace Apache.Arrow.Flight.Middleware;
2722

2823
public class ClientCookieMiddleware : IFlightClientMiddleware
2924
{
@@ -32,8 +27,6 @@ public class ClientCookieMiddleware : IFlightClientMiddleware
3227
private const string SET_COOKIE_HEADER = "Set-cookie";
3328
private const string COOKIE_HEADER = "Cookie";
3429

35-
private readonly ConcurrentDictionary<string, Cookie> _cookies = new();
36-
3730
public ClientCookieMiddleware(ClientCookieMiddlewareFactory factory,
3831
ILogger<ClientCookieMiddleware> logger)
3932
{
@@ -78,52 +71,6 @@ private string GetValidCookiesAsString()
7871
cookieList.Add(entry.Value.ToString());
7972
}
8073
}
81-
8274
return string.Join("; ", cookieList);
8375
}
84-
85-
public class ClientCookieMiddlewareFactory : IFlightClientMiddlewareFactory
86-
{
87-
public readonly ConcurrentDictionary<string, Cookie> Cookies = new(StringComparer.OrdinalIgnoreCase);
88-
private readonly ILoggerFactory _loggerFactory;
89-
90-
public ClientCookieMiddlewareFactory(ILoggerFactory loggerFactory)
91-
{
92-
_loggerFactory = loggerFactory;
93-
}
94-
95-
public IFlightClientMiddleware OnCallStarted(CallInfo callInfo)
96-
{
97-
var logger = _loggerFactory.CreateLogger<ClientCookieMiddleware>();
98-
return new ClientCookieMiddleware(this, logger);
99-
}
100-
101-
internal void UpdateCookies(IEnumerable<string> newCookieHeaderValues)
102-
{
103-
foreach (var headerValue in newCookieHeaderValues)
104-
{
105-
try
106-
{
107-
var parsedCookies = headerValue.ParseHeader();
108-
foreach (var parsedCookie in parsedCookies)
109-
{
110-
var nameLc = parsedCookie.Name.ToLower(CultureInfo.InvariantCulture);
111-
if (parsedCookie.Expired)
112-
{
113-
Cookies.TryRemove(nameLc, out _);
114-
}
115-
else
116-
{
117-
Cookies[nameLc] = parsedCookie;
118-
}
119-
}
120-
}
121-
catch (FormatException ex)
122-
{
123-
var logger = _loggerFactory.CreateLogger<ClientCookieMiddleware>();
124-
logger.LogWarning(ex, "Skipping malformed Set-Cookie header: '{HeaderValue}'", headerValue);
125-
}
126-
}
127-
}
128-
}
12976
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one or more
2+
// contributor license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright ownership.
4+
// The ASF licenses this file to You under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance with
6+
// the License. You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using System;
17+
using System.Collections.Concurrent;
18+
using System.Collections.Generic;
19+
using System.Globalization;
20+
using System.Net;
21+
using Apache.Arrow.Flight.Middleware.Extensions;
22+
using Apache.Arrow.Flight.Middleware.Interfaces;
23+
using Microsoft.Extensions.Logging;
24+
using CallInfo = Apache.Arrow.Flight.Middleware.Models.CallInfo;
25+
26+
namespace Apache.Arrow.Flight.Middleware;
27+
28+
public class ClientCookieMiddlewareFactory : IFlightClientMiddlewareFactory
29+
{
30+
public readonly ConcurrentDictionary<string, Cookie> Cookies = new(StringComparer.OrdinalIgnoreCase);
31+
private readonly ILoggerFactory _loggerFactory;
32+
33+
public ClientCookieMiddlewareFactory(ILoggerFactory loggerFactory)
34+
{
35+
_loggerFactory = loggerFactory;
36+
}
37+
38+
public IFlightClientMiddleware OnCallStarted(CallInfo callInfo)
39+
{
40+
var logger = _loggerFactory.CreateLogger<ClientCookieMiddleware>();
41+
return new ClientCookieMiddleware(this, logger);
42+
}
43+
44+
internal void UpdateCookies(IEnumerable<string> newCookieHeaderValues)
45+
{
46+
foreach (var headerValue in newCookieHeaderValues)
47+
{
48+
try
49+
{
50+
var parsedCookies = headerValue.ParseHeader();
51+
foreach (var parsedCookie in parsedCookies)
52+
{
53+
var nameLc = parsedCookie.Name.ToLower(CultureInfo.InvariantCulture);
54+
if (parsedCookie.Expired)
55+
{
56+
Cookies.TryRemove(nameLc, out _);
57+
}
58+
else
59+
{
60+
Cookies[nameLc] = parsedCookie;
61+
}
62+
}
63+
}
64+
catch (FormatException ex)
65+
{
66+
var logger = _loggerFactory.CreateLogger<ClientCookieMiddleware>();
67+
logger.LogWarning(ex, "Skipping malformed Set-Cookie header: '{HeaderValue}'", headerValue);
68+
}
69+
}
70+
}
71+
}

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Extensions/CookieExtensions.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Extensions/CookieExtensions.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,48 @@
1818
using System.Linq;
1919
using System.Net;
2020

21-
namespace Apache.Arrow.Flight.Sql.Middleware.Extensions;
21+
namespace Apache.Arrow.Flight.Middleware.Extensions;
2222

23-
// TODO: Add tests to cover: CookieExtensions
24-
internal static class CookieExtensions
23+
public static class CookieExtensions
2524
{
2625
public static IEnumerable<Cookie> ParseHeader(this string headers)
2726
{
2827
var cookies = new List<Cookie>();
29-
var segments = headers.Split(';', StringSplitOptions.RemoveEmptyEntries);
28+
if (string.IsNullOrEmpty(headers))
29+
return cookies;
30+
31+
var segments = headers.Split([';'], StringSplitOptions.RemoveEmptyEntries);
3032

3133
if (segments.Length == 0) return cookies;
3234

33-
var nameValue = segments[0].Split('=', 2);
35+
var nameValue = segments[0].Split(['='], 2);
3436
if (nameValue.Length == 2)
3537
{
36-
var cookie = new Cookie(nameValue[0], nameValue[1]);
38+
var cookie = new Cookie(nameValue[0].Trim(), nameValue[1].Trim());
39+
3740
foreach (var segment in segments.Skip(1))
3841
{
39-
if (segment.StartsWith("Expires=", StringComparison.OrdinalIgnoreCase))
42+
var trimmedSegment = segment.Trim();
43+
if (trimmedSegment.StartsWith("Expires=", StringComparison.OrdinalIgnoreCase))
4044
{
41-
if (DateTimeOffset.TryParse(segment["Expires=".Length..], out var expires))
45+
var value = trimmedSegment.Substring("Expires=".Length).Trim();
46+
47+
if (!DateTimeOffset.TryParseExact(
48+
value,
49+
"R",
50+
System.Globalization.CultureInfo.InvariantCulture,
51+
System.Globalization.DateTimeStyles.None,
52+
out var expires))
53+
{
54+
if (DateTimeOffset.TryParse(value, out expires))
55+
{
56+
cookie.Expires = expires.UtcDateTime;
57+
}
58+
}
59+
else
60+
{
4261
cookie.Expires = expires.UtcDateTime;
62+
}
4363
}
4464
}
4565

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Grpc/FlightMethodParser.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Extensions/FlightMethodParser.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
using Apache.Arrow.Flight.Sql.Middleware.Models;
16+
using Apache.Arrow.Flight.Middleware.Models;
1717

18-
namespace Apache.Arrow.Flight.Sql.Middleware.Gprc;
18+
namespace Apache.Arrow.Flight.Middleware.Extensions;
1919

20-
// TODO: Add tests to cover: FlightMethodParser
2120
public static class FlightMethodParser
2221
{
2322
/// <summary>
@@ -35,7 +34,7 @@ public static FlightMethod Parse(string fullMethodName)
3534
if (parts.Length < 2)
3635
return FlightMethod.Unknown;
3736

38-
var methodName = parts[^1];
37+
var methodName = parts[parts.Length - 1];
3938

4039
return methodName switch
4140
{
@@ -59,6 +58,6 @@ public static string ParseMethodName(string fullMethodName)
5958
return "Unknown";
6059

6160
var parts = fullMethodName.Split('/');
62-
return parts.Length >= 2 ? parts[^1] : "Unknown";
61+
return parts.Length >= 2 ? parts[parts.Length - 1] : "Unknown";
6362
}
6463
}

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Grpc/StatusUtils.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Extensions/StatusUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
using Apache.Arrow.Flight.Sql.Middleware.Models;
16+
using Apache.Arrow.Flight.Middleware.Models;
1717
using Grpc.Core;
1818

19-
namespace Apache.Arrow.Flight.Sql.Middleware.Gprc;
19+
namespace Apache.Arrow.Flight.Middleware.Extensions;
2020

2121
public static class StatusUtils
2222
{

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Interceptors/ClientInterceptorAdapter.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Interceptors/ClientInterceptorAdapter.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19-
using Apache.Arrow.Flight.Sql.Middleware.Gprc;
20-
using Apache.Arrow.Flight.Sql.Middleware.Grpc;
21-
using Apache.Arrow.Flight.Sql.Middleware.Interfaces;
22-
using Apache.Arrow.Flight.Sql.Middleware.Models;
19+
using Apache.Arrow.Flight.Middleware.Extensions;
20+
using Apache.Arrow.Flight.Middleware.Interfaces;
2321
using Grpc.Core;
2422
using Grpc.Core.Interceptors;
23+
using CallInfo = Apache.Arrow.Flight.Middleware.Models.CallInfo;
2524

26-
namespace Apache.Arrow.Flight.Sql.Middleware.Interceptors
25+
namespace Apache.Arrow.Flight.Middleware.Interceptors
2726
{
2827
public class ClientInterceptorAdapter : Interceptor
2928
{
@@ -36,8 +35,7 @@ public ClientInterceptorAdapter(IEnumerable<IFlightClientMiddlewareFactory> fact
3635

3736
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
3837
TRequest request,
39-
ClientInterceptorContext<TRequest, TResponse> context,
40-
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
38+
ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
4139
where TRequest : class
4240
where TResponse : class
4341
{

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Interfaces/ICallHeaders.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Interfaces/ICallHeaders.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515

1616
using System.Collections.Generic;
1717

18-
namespace Apache.Arrow.Flight.Sql.Middleware.Interfaces;
18+
namespace Apache.Arrow.Flight.Middleware.Interfaces;
1919

2020
public interface ICallHeaders
2121
{
22-
string? this[string key] { get; }
22+
string this[string key] { get; }
2323

24-
string? Get(string key);
25-
byte[]? GetBytes(string key);
24+
string Get(string key);
25+
byte[] GetBytes(string key);
2626
IEnumerable<string> GetAll(string key);
2727
IEnumerable<byte[]> GetAllBytes(string key);
2828

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Interfaces/IFlightClientMiddleware.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Interfaces/IFlightClientMiddleware.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
using Apache.Arrow.Flight.Sql.Middleware.Models;
16+
using Apache.Arrow.Flight.Middleware.Models;
17+
using CallInfo = Apache.Arrow.Flight.Middleware.Models.CallInfo;
1718

18-
namespace Apache.Arrow.Flight.Sql.Middleware.Interfaces;
19+
namespace Apache.Arrow.Flight.Middleware.Interfaces;
1920

2021
public interface IFlightClientMiddleware
2122
{

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Grpc/MetadataAdapter.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/MetadataAdapter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19-
using Apache.Arrow.Flight.Sql.Middleware.Interfaces;
19+
using Apache.Arrow.Flight.Middleware.Interfaces;
2020
using Grpc.Core;
2121

22-
namespace Apache.Arrow.Flight.Sql.Middleware.Grpc;
22+
namespace Apache.Arrow.Flight.Middleware;
2323

2424
public class MetadataAdapter : ICallHeaders
2525
{
@@ -30,15 +30,15 @@ public MetadataAdapter(Metadata metadata)
3030
_metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
3131
}
3232

33-
public string? this[string key] => Get(key);
33+
public string this[string key] => Get(key);
3434

35-
public string? Get(string key)
35+
public string Get(string key)
3636
{
3737
return _metadata.FirstOrDefault(e =>
3838
!e.IsBinary && e.Key.Equals(key, StringComparison.OrdinalIgnoreCase))?.Value;
3939
}
4040

41-
public byte[]? GetBytes(string key)
41+
public byte[] GetBytes(string key)
4242
{
4343
return _metadata.FirstOrDefault(e =>
4444
e.IsBinary && e.Key.Equals(NormalizeBinaryKey(key), StringComparison.OrdinalIgnoreCase))?.ValueBytes;
@@ -88,13 +88,13 @@ private static string NormalizeBinaryKey(string key)
8888

8989
private static string DenormalizeBinaryKey(string key)
9090
=> key.EndsWith(Metadata.BinaryHeaderSuffix, StringComparison.OrdinalIgnoreCase)
91-
? key[..^Metadata.BinaryHeaderSuffix.Length]
91+
? key.Substring(0, key.Length - Metadata.BinaryHeaderSuffix.Length)
9292
: key;
9393
}
9494

9595
public static class MetadataAdapterExtensions
9696
{
97-
public static bool TryGet(this ICallHeaders headers, string key, out string? value)
97+
public static bool TryGet(this ICallHeaders headers, string key, out string value)
9898
{
9999
value = headers.Get(key);
100100
return value is not null;

csharp/src/Apache.Arrow.Flight.Sql/Middleware/Models/CallInfo.cs renamed to csharp/src/Apache.Arrow.Flight/Middleware/Models/CallInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
using System;
1717

18-
namespace Apache.Arrow.Flight.Sql.Middleware.Models;
18+
namespace Apache.Arrow.Flight.Middleware.Models;
1919

2020
public sealed class CallInfo
2121
{

0 commit comments

Comments
 (0)