forked from EvilBeaver/OneScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathASPNETHandler.cs
More file actions
313 lines (265 loc) · 13.2 KB
/
ASPNETHandler.cs
File metadata and controls
313 lines (265 loc) · 13.2 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*----------------------------------------------------------
This Source Code Form is subject to the terms of the
Mozilla Public License, v.2.0. If a copy of the MPL
was not distributed with this file, You can obtain one
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Web;
using System.Runtime.Caching;
using System.IO;
using ScriptEngine.Machine;
using ScriptEngine.Environment;
using ScriptEngine.HostedScript;
using System.Runtime.CompilerServices;
namespace OneScript.ASPNETHandler
{
public class ASPNETHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
HostedScriptEngine _hostedScript;
// Разрешает или запрещает кэширование исходников *.os В Linux должно быть false иначе после изменений исходника старая версия будет в кэше
// web.config -> <appSettings> -> <add key="CachingEnabled" value="true"/>
static bool _cachingEnabled;
// Список дополнительных сборок, которые надо приаттачить к движку. Могут быть разные расширения
// web.config -> <appSettings> -> <add key="ASPNetHandler" value="attachAssembly"/> Сделано так для простоты. Меньше настроек - дольше жизнь :)
static List<System.Reflection.Assembly> _assembliesForAttaching;
public bool IsReusable
{
// Разрешаем повторное использование и храним среду выполнения и контекст
get { return true; }
}
static ASPNETHandler()
{
_assembliesForAttaching = new List<System.Reflection.Assembly>();
System.Collections.Specialized.NameValueCollection appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings;
TextWriter logWriter = OpenLog(appSettings);
_cachingEnabled = (appSettings["cachingEnabled"] == "true");
WriteToLog(logWriter, "Start assemblies loading.");
foreach (string assemblyName in appSettings.AllKeys)
{
if (appSettings[assemblyName] == "attachAssembly")
{
try
{
_assembliesForAttaching.Add(System.Reflection.Assembly.Load(assemblyName));
}
// TODO: Исправить - должно падать. Если конфиг сайта неработоспособен - сайт не должен быть работоспособен.
catch(Exception ex)
{
WriteToLog(logWriter, "Error loading assembly: " + assemblyName + " " + ex.ToString());
if (appSettings["handlerLoadingPolicy"] == "strict")
throw; // Must fail!
}
}
}
WriteToLog(logWriter, "Stop assemblies loading.");
CloseLog(logWriter);
}
public ASPNETHandler()
{
System.Collections.Specialized.NameValueCollection appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings;
// Инициализируем логгирование, если надо
TextWriter logWriter = OpenLog(appSettings);
WriteToLog(logWriter, "Start loading.");
try
{
_hostedScript = new HostedScriptEngine();
// метод настраивает внутренние переменные у SystemGlobalContext
if (appSettings["enableEcho"] == "true")
_hostedScript.SetGlobalEnvironment(new ASPNetApplicationHost(), new AspEntryScriptSrc(appSettings["startupScript"] ?? HttpContext.Current.Server.MapPath("~/web.config")));
else
_hostedScript.SetGlobalEnvironment(new AspNetNullApplicationHost(), new AspNetNullEntryScriptSrc());
_hostedScript.Initialize();
// Размещаем oscript.cfg вместе с web.config. Так наверное привычнее
_hostedScript.CustomConfig = appSettings["configFilePath"] ?? HttpContext.Current.Server.MapPath("~/oscript.cfg");
_hostedScript.AttachAssembly(System.Reflection.Assembly.GetExecutingAssembly());
// Аттачим доп сборки. По идее должны лежать в Bin
foreach (System.Reflection.Assembly assembly in _assembliesForAttaching)
{
try
{
_hostedScript.AttachAssembly(assembly);
}
catch (Exception ex)
{
// Возникла проблема при аттаче сборки
WriteToLog(logWriter, "Assembly attaching error: " + ex.Message);
if (appSettings["handlerLoadingPolicy"] == "strict")
throw;
}
}
//Загружаем библиотечные скрипты aka общие модули
string libPath = appSettings["commonModulesPath"];
if (libPath != null)
{
libPath = HttpContext.Current.Server.MapPath(libPath);
string[] files = System.IO.Directory.GetFiles(libPath, "*.os");
foreach (string filePathName in files)
{
_hostedScript.InjectGlobalProperty(System.IO.Path.GetFileNameWithoutExtension(filePathName), ValueFactory.Create(), true);
}
foreach (string filePathName in files)
{
try
{
ICodeSource src = _hostedScript.Loader.FromFile(filePathName);
var compilerService = _hostedScript.GetCompilerService();
var module = compilerService.Compile(src);
var loaded = _hostedScript.EngineInstance.LoadModuleImage(module);
var instance = (IValue)_hostedScript.EngineInstance.NewObject(loaded);
_hostedScript.EngineInstance.Environment.SetGlobalProperty(System.IO.Path.GetFileNameWithoutExtension(filePathName), instance);
}
catch (Exception ex)
{
// Возникла проблема при загрузке файла os, логгируем, если логгирование включено
WriteToLog(logWriter, "Error loading " + System.IO.Path.GetFileNameWithoutExtension(filePathName) + " : " + ex.Message);
if (appSettings["handlerLoadingPolicy"] == "strict")
throw;
}
}
}
_hostedScript.EngineInstance.Environment.LoadMemory(MachineInstance.Current);
}
catch (Exception ex)
{
// Возникла проблема при инициализации хэндлера
WriteToLog(logWriter, ex.Message);
if (appSettings["handlerLoadingPolicy"] == "strict")
throw; // Must fail!
}
finally
{
WriteToLog(logWriter, "End loading.");
CloseLog(logWriter);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static TextWriter OpenLog(System.Collections.Specialized.NameValueCollection appSettings = null)
{
if (appSettings == null)
appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings;
string logPath = appSettings["logToPath"];
try
{
if (logPath != null)
{
logPath = HttpContext.Current.Server.MapPath(logPath);
string logFileName = Guid.NewGuid().ToString().Replace("-", "") + ".txt";
return File.CreateText(Path.Combine(logPath, logFileName));
}
else
return null;
}
catch
{
return null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void WriteToLog(TextWriter logWriter, string message)
{
if (logWriter == null)
return;
try
{
logWriter.WriteLine(message);
}
catch { /* что-то не так, ничего не делаем */ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void CloseLog(TextWriter logWriter)
{
if (logWriter != null)
{
try
{
logWriter.Flush();
logWriter.Close();
}
catch
{ /*что-то не так, ничего не делаем.*/ }
}
}
public void ProcessRequest(HttpContext context)
{
CallScriptHandler(context);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ProduceResponse(HttpContext context, IRuntimeContextInstance runner)
{
int methodIndex = runner.FindMethod("ОбработкаВызоваHTTPСервиса");
IValue result;
IValue[] args = new IValue[1];
args[0] = new ScriptEngine.HostedScript.Library.HTTPService.HTTPServiceRequestImpl(context);
runner.CallAsFunction(methodIndex, args, out result);
// Обрабатываем результаты
var response = (ScriptEngine.HostedScript.Library.HTTPService.HTTPServiceResponseImpl) result;
context.Response.StatusCode = response.StatusCode;
if (response.Headers != null)
{
foreach (var ch in response.Headers)
{
context.Response.AddHeader(ch.Key.AsString(), ch.Value.AsString());
}
}
if (response.Reason != "")
{
context.Response.Status = response.Reason;
}
if (response.BodyStream != null)
{
response.BodyStream.Seek(0, SeekOrigin.Begin);
response.BodyStream.CopyTo(context.Response.OutputStream);
}
context.Response.Charset = response.ContentCharset;
context.Response.End();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private IRuntimeContextInstance CreateServiceInstance(LoadedModule module)
{
var runner = _hostedScript.EngineInstance.NewObject(module);
return runner;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private LoadedModule LoadByteCode(string filePath)
{
var code = _hostedScript.EngineInstance.Loader.FromFile(filePath);
var compiler = _hostedScript.GetCompilerService();
var byteCode = compiler.Compile(code);
var module = _hostedScript.EngineInstance.LoadModuleImage(byteCode);
return module;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CallScriptHandler(HttpContext context)
{
_hostedScript.EngineInstance.Environment.LoadMemory(MachineInstance.Current);
#region Загружаем скрипт (файл .os)
// Кэшируем исходный файл, если файл изменился (изменили скрипт .os) загружаем заново
// В Linux под Mono не работает подписка на изменение файла.
LoadedModule module = null;
ObjectCache cache = MemoryCache.Default;
if (_cachingEnabled)
{
module = cache[context.Request.PhysicalPath] as LoadedModule;
if (module == null)
{
CacheItemPolicy policy = new CacheItemPolicy();
List<string> filePaths = new List<string>();
filePaths.Add(context.Request.PhysicalPath);
policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths));
// Загружаем файл и помещаем его в кэш
module = LoadByteCode(context.Request.PhysicalPath);
cache.Set(context.Request.PhysicalPath, module, policy);
}
}
else
{
module = LoadByteCode(context.Request.PhysicalPath);
}
#endregion
var runner = CreateServiceInstance(module);
ProduceResponse(context, runner);
}
}
}