forked from bonesoul/uhttpsharp
-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathClassRouter.cs
More file actions
160 lines (128 loc) · 6.02 KB
/
Copy pathClassRouter.cs
File metadata and controls
160 lines (128 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace uhttpsharp.Handlers
{
public class ClassRouter : IHttpRequestHandler
{
private static readonly ConcurrentDictionary<Tuple<Type, string>, Func<IHttpRequestHandler, IHttpRequestHandler>>
Routers = new ConcurrentDictionary<Tuple<Type, string>, Func<IHttpRequestHandler, IHttpRequestHandler>>();
private static readonly ConcurrentDictionary<Type, Func<IHttpRequestHandler, string, Task<IHttpRequestHandler>>>
IndexerRouters = new ConcurrentDictionary<Type, Func<IHttpRequestHandler, string, Task<IHttpRequestHandler>>>();
private readonly IHttpRequestHandler _root;
public ClassRouter(IHttpRequestHandler root)
{
_root = root;
}
public async Task Handle(IHttpContext context, Func<Task> next)
{
var handler = _root;
foreach (var parameter in context.Request.RequestParameters)
{
var getNextHandler = Routers.GetOrAdd(Tuple.Create(handler.GetType(), parameter), CreateRoute);
if (getNextHandler != null)
{
handler = getNextHandler(handler);
}
else
{
var getNextByIndex = IndexerRouters.GetOrAdd(handler.GetType(), GetIndexerRouter);
if (getNextByIndex == null)
{
await next();
return;
}
var returnedTask = getNextByIndex(handler, parameter);
if (returnedTask == null)
{
await next();
return;
}
handler = await returnedTask;
}
// Incase that one of the methods returned null (Indexer / Getter)
if (handler == null)
{
await next();
return;
}
}
await handler.Handle(context, next);
}
private Func<IHttpRequestHandler, string, Task<IHttpRequestHandler>> GetIndexerRouter(Type arg)
{
var indexer = GetIndexer(arg);
if (indexer == null)
{
return null;
}
var parameterType = indexer.GetParameters()[0].ParameterType;
var inputHandler = Expression.Parameter(typeof(IHttpRequestHandler));
var inputObject = Expression.Parameter(typeof(string));
var tryParseMethod = parameterType.GetMethod("TryParse", new[] { typeof(string), parameterType.MakeByRefType() });
Expression body;
if (tryParseMethod == null)
{
var handlerConverted = Expression.Convert(inputHandler, arg);
var objectConverted =
Expression.Convert(
Expression.Call(typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }), inputObject,
Expression.Constant(parameterType)), parameterType);
var indexerExpression = Expression.Call(handlerConverted, indexer, objectConverted);
var returnValue = Expression.Convert(indexerExpression, typeof(IHttpRequestHandler));
body = returnValue;
}
else
{
var inputConvertedVar = Expression.Variable(parameterType, "inputObjectConverted");
var handlerConverted = Expression.Convert(inputHandler, arg);
var objectConverted = inputConvertedVar;
var indexerExpression = Expression.Call(handlerConverted, indexer, objectConverted);
var returnValue = Expression.Convert(indexerExpression, typeof(Task<IHttpRequestHandler>));
var returnTarget = Expression.Label(typeof(Task<IHttpRequestHandler>));
var returnLabel = Expression.Label(returnTarget, Expression.Convert(Expression.Constant(null), typeof(Task<IHttpRequestHandler>)));
body =
Expression.Block(
new[] { inputConvertedVar },
Expression.IfThen(
Expression.Call(tryParseMethod, inputObject,
inputConvertedVar),
Expression.Return(returnTarget, returnValue)
),
returnLabel);
}
return Expression.Lambda<Func<IHttpRequestHandler, string, Task<IHttpRequestHandler>>>(body, inputHandler,
inputObject).Compile();
}
private MethodInfo GetIndexer(Type arg)
{
var indexer =
arg.GetMethods().SingleOrDefault(m => Attribute.IsDefined(m, typeof(IndexerAttribute))
&& m.GetParameters().Length == 1
&& typeof(Task<IHttpRequestHandler>).IsAssignableFrom(m.ReturnType));
return indexer;
}
private Func<IHttpRequestHandler, IHttpRequestHandler> CreateRoute(Tuple<Type, string> arg)
{
var parameter = Expression.Parameter(typeof(IHttpRequestHandler), "input");
var converted = Expression.Convert(parameter, arg.Item1);
var propertyInfo = arg.Item1.GetProperty(arg.Item2);
if (propertyInfo == null)
{
return null;
}
var property = Expression.Property(converted, propertyInfo);
var propertyConverted = Expression.Convert(property, typeof(IHttpRequestHandler));
return Expression.Lambda<Func<IHttpRequestHandler, IHttpRequestHandler>>(propertyConverted, new[] { parameter }).Compile();
}
}
public class IndexerAttribute : Attribute
{
}
}