SerslotServer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Microsoft.AspNetCore.Hosting;
  8. using Microsoft.AspNetCore.Hosting.Server;
  9. using Microsoft.AspNetCore.Http;
  10. using Microsoft.AspNetCore.Http.Features;
  11. using Vit.Core.Module.Log;
  12. using Vit.Extensions;
  13. using Vit.Extensions.ObjectExt;
  14. namespace Sers.Serslot
  15. {
  16. public class SerslotServer : IServer
  17. {
  18. /// <summary>
  19. ///
  20. /// </summary>
  21. public IServiceProvider serviceProvider { get; set; }
  22. #region PairingToken
  23. string pairingToken;
  24. public void InitPairingToken(IWebHostBuilder hostBuilder)
  25. {
  26. //search "MS-ASPNETCORE-TOKEN" to know why
  27. string PairingToken = "TOKEN";
  28. pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}");
  29. }
  30. #endregion
  31. #region ProcessRequest
  32. Action<FeatureCollection> OnProcessRequest;
  33. public IHttpResponseFeature ProcessRequest(HttpRequestFeature requestFeature)
  34. {
  35. if (requestFeature.Headers == null)
  36. requestFeature.Headers = new HeaderDictionary();
  37. //var header = "{\"Cache-Control\":\"max-age=0\",\"Connection\":\"Keep-Alive\",\"Accept\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\",\"Accept-Encoding\":\"gzip, deflate\",\"Accept-Language\":\"zh-CN,zh;q=0.8\",\"Host\":\"localhost:44308\",\"User-Agent\":\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0\",\"Upgrade-Insecure-Requests\":\"1\",\"X-Forwarded-For\":\"127.0.0.1:53093\",\"X-Forwarded-Proto\":\"https\"}";
  38. //header = "{\"Host\":\"localhost:44308\",\"X-Forwarded-For\":\"127.0.0.1:53093\",\"X-Forwarded-Proto\":\"https\"}";
  39. //ʹÓÃAdd¿ÉÄܱ¨´í An item with the same key has already been added. Key: X-Forwarded-Proto"
  40. //requestFeature.Headers.Add("MS-ASPNETCORE-TOKEN", pairingToken);
  41. //requestFeature.Headers.Add("X-Forwarded-Proto", "https");
  42. requestFeature.Headers["MS-ASPNETCORE-TOKEN"] = pairingToken;
  43. requestFeature.Headers["X-Forwarded-Proto"] = "https";
  44. var features = new FeatureCollection();
  45. features.Set<IHttpRequestFeature>(requestFeature);
  46. //var _responseFeature = new SerslotResponseFeature() { Body = new MemoryStream() };
  47. var _responseFeature = new HttpResponseFeature() { Body = new MemoryStream() };
  48. features.Set<IHttpResponseFeature>(_responseFeature);
  49. //IHttpResponseBodyFeature
  50. if (Type_IResponseBodyFeature != null)
  51. {
  52. features[Type_IResponseBodyFeature] = Activator.CreateInstance(Type_ResponseBodyFeature, _responseFeature.Body);
  53. }
  54. OnProcessRequest(features);
  55. return _responseFeature;
  56. }
  57. #region SerslotResponseFeature
  58. class SerslotResponseFeature : IHttpResponseFeature
  59. {
  60. public SerslotResponseFeature()
  61. {
  62. StatusCode = 200;
  63. Headers = new HeaderDictionary();
  64. Body = Stream.Null;
  65. }
  66. public int StatusCode
  67. {
  68. get;
  69. set;
  70. }
  71. public string ReasonPhrase
  72. {
  73. get;
  74. set;
  75. }
  76. public IHeaderDictionary Headers
  77. {
  78. get;
  79. set;
  80. }
  81. public Stream Body
  82. {
  83. get;
  84. set;
  85. }
  86. public virtual bool HasStarted { get; set; } = false;
  87. private Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
  88. private Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
  89. #region OnStarting
  90. public virtual void OnStarting(Func<object, Task> callback, object state)
  91. {
  92. lock (this)
  93. {
  94. if (HasStarted)
  95. {
  96. throw new InvalidOperationException(nameof(OnStarting));
  97. }
  98. if (_onStarting == null)
  99. {
  100. _onStarting = new Stack<KeyValuePair<Func<object, Task>, object>>();
  101. }
  102. _onStarting.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
  103. }
  104. }
  105. public Task FireOnStarting()
  106. {
  107. Stack<KeyValuePair<Func<object, Task>, object>> onStarting;
  108. lock (this)
  109. {
  110. onStarting = _onStarting;
  111. _onStarting = null;
  112. }
  113. if (onStarting == null)
  114. {
  115. return Task.CompletedTask;
  116. }
  117. else
  118. {
  119. return FireOnStartingMayAwait(onStarting);
  120. }
  121. }
  122. private Task FireOnStartingMayAwait(Stack<KeyValuePair<Func<object, Task>, object>> onStarting)
  123. {
  124. try
  125. {
  126. var count = onStarting.Count;
  127. for (var i = 0; i < count; i++)
  128. {
  129. var entry = onStarting.Pop();
  130. var task = entry.Key.Invoke(entry.Value);
  131. if (!ReferenceEquals(task, Task.CompletedTask))
  132. {
  133. return FireOnStartingAwaited(task, onStarting);
  134. }
  135. }
  136. }
  137. catch (Exception ex)
  138. {
  139. Logger.Error(ex);
  140. }
  141. return Task.CompletedTask;
  142. }
  143. private async Task FireOnStartingAwaited(Task currentTask, Stack<KeyValuePair<Func<object, Task>, object>> onStarting)
  144. {
  145. try
  146. {
  147. await currentTask;
  148. var count = onStarting.Count;
  149. for (var i = 0; i < count; i++)
  150. {
  151. var entry = onStarting.Pop();
  152. await entry.Key.Invoke(entry.Value);
  153. }
  154. }
  155. catch (Exception ex)
  156. {
  157. Logger.Error(ex);
  158. }
  159. }
  160. #endregion
  161. #region OnCompleted
  162. public virtual void OnCompleted(Func<object, Task> callback, object state)
  163. {
  164. lock (this)
  165. {
  166. if (onCompleted == null)
  167. {
  168. onCompleted = new Stack<KeyValuePair<Func<object, Task>, object>>();
  169. }
  170. onCompleted.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
  171. }
  172. }
  173. Stack<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
  174. public Task FireOnCompleted()
  175. {
  176. Stack<KeyValuePair<Func<object, Task>, object>> onCompleted;
  177. lock (this)
  178. {
  179. onCompleted = _onCompleted;
  180. _onCompleted = null;
  181. }
  182. if (onCompleted == null)
  183. {
  184. return Task.CompletedTask;
  185. }
  186. return FireOnCompletedAwaited(onCompleted);
  187. }
  188. private async Task FireOnCompletedAwaited(Stack<KeyValuePair<Func<object, Task>, object>> onCompleted)
  189. {
  190. foreach (var entry in onCompleted)
  191. {
  192. try
  193. {
  194. await entry.Key.Invoke(entry.Value);
  195. }
  196. catch (Exception ex)
  197. {
  198. Logger.Error(ex);
  199. }
  200. }
  201. }
  202. #endregion
  203. }
  204. #endregion
  205. #endregion
  206. Type Type_IResponseBodyFeature = Vit.Core.Util.Reflection.ObjectLoader.GetType("Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature", assemblyName: "Microsoft.AspNetCore.Http.Features");
  207. Type Type_ResponseBodyFeature = Vit.Core.Util.Reflection.ObjectLoader.GetType("Microsoft.AspNetCore.Http.StreamResponseBodyFeature", assemblyName: "Microsoft.AspNetCore.Http");
  208. public IFeatureCollection Features { get; } = new FeatureCollection();
  209. public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
  210. {
  211. try
  212. {
  213. #region (x.1) build OnProcessRequest
  214. OnProcessRequest = (features) =>
  215. {
  216. Exception _applicationException = null;
  217. var httpContext = application.CreateContext(features);
  218. try
  219. {
  220. //var httpContext_ = httpContext.GetProperty<object>("HttpContext");
  221. //if (httpContext_ is Microsoft.AspNetCore.Http.HttpContext defaultHttpContext)
  222. //{
  223. // //if (defaultHttpContext.Response.Body == null)
  224. // defaultHttpContext.Response.Body = features.Get<IHttpResponseFeature>().Body;
  225. //}
  226. // Run the application code for this request
  227. // application.ProcessRequestAsync(httpContext).GetAwaiter().GetResult();
  228. application.ProcessRequestAsync(httpContext).Wait();
  229. //var _responseFeature = features.Get<IHttpResponseFeature>() as SerslotResponseFeature;
  230. //if (_responseFeature != null)
  231. //{
  232. // _responseFeature.FireOnStarting();
  233. // _responseFeature.FireOnCompleted();
  234. //}
  235. }
  236. catch (Exception ex)
  237. {
  238. _applicationException = ex;
  239. Logger.Error(ex);
  240. }
  241. application.DisposeContext(httpContext, _applicationException);
  242. };
  243. #endregion
  244. #region (x.2) start ServiceStation
  245. #region (x.x.1) Init
  246. ServiceStation.ServiceStation.Init();
  247. Sers.Core.Module.App.SersApplication.onStop += () =>
  248. {
  249. if (serviceProvider.GetService(typeof(IApplicationLifetime)) is IApplicationLifetime lifetime)
  250. {
  251. lifetime.StopApplication();
  252. }
  253. };
  254. #endregion
  255. #region (x.x.2)¼ÓÔØapi
  256. ServiceStation.ServiceStation.Instance.LoadApi();
  257. ServiceStation.ServiceStation.Instance.localApiService.LoadSerslotApi(Assembly.GetEntryAssembly(), this);
  258. #endregion
  259. //(x.x.3)Start ServiceStation
  260. if (!ServiceStation.ServiceStation.Start())
  261. {
  262. Dispose();
  263. }
  264. #endregion
  265. }
  266. catch (Exception ex)
  267. {
  268. Dispose();
  269. throw;
  270. }
  271. }
  272. // Graceful shutdown if possible
  273. public async Task StopAsync(CancellationToken cancellationToken)
  274. {
  275. try
  276. {
  277. ServiceStation.ServiceStation.Stop();
  278. }
  279. catch (Exception ex)
  280. {
  281. Logger.Error(ex);
  282. }
  283. }
  284. // Ungraceful shutdown
  285. public void Dispose()
  286. {
  287. var cancelledTokenSource = new CancellationTokenSource();
  288. cancelledTokenSource.Cancel();
  289. StopAsync(cancelledTokenSource.Token).GetAwaiter().GetResult();
  290. }
  291. }
  292. }