You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

862 lines
32 KiB

4 years ago
4 years ago
4 years ago
4 years ago
  1. using Apewer;
  2. using Apewer.Models;
  3. using Apewer.Network;
  4. using Apewer.Source;
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Net;
  11. using System.Reflection;
  12. using System.Text;
  13. #if NETFX
  14. using System.Web;
  15. #endif
  16. #if NETCORE
  17. using Microsoft.AspNetCore.Http;
  18. #endif
  19. namespace Apewer.Web
  20. {
  21. internal abstract class ApiProcessor
  22. {
  23. // 请求 Body 大于 100MB 时,不尝试解析 API。
  24. const long MaxRequestBody = 100L * 1024L * 1024L;
  25. #region fields & properties
  26. internal ApiInvoker Invoker = null;
  27. private DateTime Beginning = DateTime.Now;
  28. private DateTime Ending;
  29. private ApiRequest ApiRequest;
  30. private ApiResponse ApiResponse;
  31. public Dictionary<string, ApiApplication> Entries;
  32. public bool HaveContext { get; protected set; }
  33. // 抽象方法的返回值。
  34. private Uri Url;
  35. private HttpMethod Method;
  36. protected abstract object GetContext();
  37. protected abstract string GetClientIP();
  38. protected abstract HttpMethod GetMethod();
  39. protected abstract Uri GetURL();
  40. protected abstract Uri GetReferrer();
  41. protected abstract StringPairs GetHeaders();
  42. protected abstract StringPairs GetCookies();
  43. protected abstract long GetRequestLength();
  44. protected abstract byte[] ReadRequest();
  45. protected abstract void SetCacheControl(int seconds);
  46. protected abstract void SetContentType(string value);
  47. protected abstract void SetContentLength(long value);
  48. protected abstract void Redirect(string url);
  49. protected abstract string AddHeader(string name, string value);
  50. protected abstract void WriteResponse(byte[] bytes);
  51. protected abstract void WriteResponse(Stream stream);
  52. #endregion
  53. /// <summary>执行处理程序,返回错误信息。</summary>
  54. public string Run()
  55. {
  56. try
  57. {
  58. // 检查依赖的属性,若不通过,则无法执行。
  59. var preCheckError = PreCheck();
  60. if (!string.IsNullOrEmpty(preCheckError)) return preCheckError;
  61. // 准备变量。
  62. ApiRequest = GetRequest();
  63. ApiResponse = new ApiResponse();
  64. // 准备向 Controller 传递的属性。
  65. var an = ApiRequest.Application;
  66. var a = null as ApiApplication;
  67. var fn = ApiRequest.Function;
  68. var f = null as ApiFunction;
  69. var r = ApiRequest.Random;
  70. var t = ApiRequest.Ticket;
  71. // 调用。
  72. if (Entries.ContainsKey(an))
  73. {
  74. a = Entries[an];
  75. if (a.Functions.ContainsKey(fn))
  76. {
  77. f = a.Functions[fn];
  78. Invoke(a, f, ApiRequest, ApiResponse);
  79. }
  80. else
  81. {
  82. Invoke(a, null, ApiRequest, ApiResponse);
  83. }
  84. }
  85. else
  86. {
  87. var @default = ApiOptions.Default;
  88. if (@default != null)
  89. {
  90. Invoke(ApiInvoker.GetApplication(@default, false), null, ApiRequest, ApiResponse);
  91. }
  92. else
  93. {
  94. ApiResponse.Error("指定的 Application 无效。");
  95. if (an.IsEmpty() && ApiOptions.AllowEnumerate) Enumerate(Entries);
  96. }
  97. }
  98. // 记录结束时间。
  99. Ending = DateTime.Now;
  100. // 调整响应。
  101. ApiResponse.Beginning = Beginning;
  102. ApiResponse.Ending = Ending;
  103. ApiResponse.Application = an;
  104. ApiResponse.Function = fn;
  105. ApiResponse.Random = r;
  106. // 向客户端输出。
  107. return Output(ApiResponse);
  108. }
  109. catch (Exception ex)
  110. {
  111. Logger.Web.Exception(this, ex);
  112. return ex.Message;
  113. }
  114. }
  115. // 检查执行的前提条件。
  116. string PreCheck()
  117. {
  118. // 检查条件。
  119. if (!HaveContext) return "Context 无效。";
  120. if (Entries == null) return "Entries 无效。";
  121. Method = GetMethod();
  122. if (Method == HttpMethod.NULL) return "Methods 无效。";
  123. // 响应标头。
  124. if (ApiOptions.WithAccessControl)
  125. {
  126. AddHeader("Access-Control-Allow-Headers", "Content-Type");
  127. AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  128. AddHeader("Access-Control-Allow-Origin", "*");
  129. var maxage = ApiOptions.AccessControlMaxAge;
  130. if (maxage > 0) AddHeader("Access-Control-Max-Age", maxage.ToString());
  131. }
  132. if (ApiOptions.AllowContentTypeOptions || ApiOptions.Default != null)
  133. {
  134. AddHeader("X-Content-Type-Options", "nosniff");
  135. }
  136. // 在此之后可以输出
  137. // URL
  138. Url = GetURL();
  139. var lowerPath = TextUtility.ToLower(Url.AbsolutePath);
  140. // favicon.ico
  141. if (!ApiOptions.AllowFavIcon)
  142. {
  143. if (lowerPath.StartsWith("/favicon.ico"))
  144. {
  145. SetCacheControl(0);
  146. Output(null as byte[]);
  147. return "已取消对 favicon.ico 的请求。";
  148. }
  149. }
  150. // robots.txt
  151. if (!ApiOptions.AllowRobots)
  152. {
  153. if (lowerPath.StartsWith("/robots.txt"))
  154. {
  155. const string text = "User-agent: *\nDisallow: / \n";
  156. SetCacheControl(0);
  157. Output(text);
  158. return "已取消对 robots.txt 的请求。";
  159. }
  160. }
  161. return null;
  162. }
  163. void Enumerate(Dictionary<string, ApiApplication> applications)
  164. {
  165. var count = 0;
  166. var list = Json.NewArray();
  167. if (applications != null)
  168. {
  169. foreach (var i in applications)
  170. {
  171. if (!i.Value.Visible) continue;
  172. var item = Json.NewObject();
  173. item["name"] = i.Value.Name;
  174. item["caption"] = i.Value.Caption;
  175. if (ApiOptions.WithModuleName) item["module"] = i.Value.Module;
  176. if (ApiOptions.WithTypeName) item["type"] = i.Value.Type.FullName;
  177. list.AddItem(item);
  178. count = count + 1;
  179. }
  180. }
  181. ApiResponse.Data.Reset(Json.NewObject());
  182. ApiResponse.Data.SetProperty("count", count);
  183. ApiResponse.Data.SetProperty("list", list);
  184. }
  185. void Enumerate(Dictionary<string, ApiFunction> functions)
  186. {
  187. var count = 0;
  188. var list = Json.NewArray();
  189. if (functions != null)
  190. {
  191. foreach (var i in functions)
  192. {
  193. if (!i.Value.Visible) continue;
  194. var item = Json.NewObject();
  195. item["name"] = i.Value.Name;
  196. item["caption"] = i.Value.Caption;
  197. item["description"] = i.Value.Description;
  198. list.AddItem(item);
  199. count = count + 1;
  200. }
  201. }
  202. ApiResponse.Data.Reset(Json.NewObject());
  203. ApiResponse.Data.SetProperty("count", count);
  204. ApiResponse.Data.SetProperty("list", list);
  205. }
  206. internal static ApiController CreateController(Type type)
  207. {
  208. try
  209. {
  210. return (ApiController)Activator.CreateInstance(type);
  211. }
  212. catch { }
  213. return null;
  214. }
  215. void Invoke(ApiApplication application, ApiFunction function, ApiRequest request, ApiResponse response)
  216. {
  217. if (request == null || response == null) return;
  218. // 创建控制器实例。
  219. if (application == null)
  220. {
  221. response.Error("指定的 Application 无效,无法创建实例。");
  222. return;
  223. }
  224. var controller = CreateController(application.Type);
  225. if (controller == null)
  226. {
  227. response.Error("创建控制器实例失败。");
  228. return;
  229. }
  230. controller.Application = application;
  231. controller.Function = function;
  232. controller.Request = request;
  233. controller.Response = response;
  234. Invoke(controller);
  235. }
  236. void Invoke(ApiController controller)
  237. {
  238. if (controller == null) return;
  239. var application = controller.Application;
  240. var function = controller.Function;
  241. var request = controller.Request;
  242. var response = controller.Response;
  243. if (application == null) return;
  244. if (request == null || response == null) return;
  245. // 调用功能。
  246. try
  247. {
  248. if (controller.AfterInitialized != null) controller.AfterInitialized.Invoke();
  249. // 控制器要求单独处理,此时框架不再处理 Function。
  250. var allowFunction = controller.AllowFunction;
  251. if (application.Independent) allowFunction = false;
  252. // 由框架调用 Function。
  253. if (allowFunction)
  254. {
  255. if (function == null)
  256. {
  257. var @default = controller.DefaultFunction;
  258. if (@default != null)
  259. {
  260. @default.Invoke();
  261. }
  262. else
  263. {
  264. controller.Response.Error("指定的 Function 无效。");
  265. if (ApiOptions.AllowEnumerate) Enumerate(application.Functions);
  266. }
  267. }
  268. else
  269. {
  270. var result = function.Method.Invoke(controller, null);
  271. if (response.Status.IsBlank()) response.Status = "ok";
  272. if (function.Returnable && result != null)
  273. {
  274. if (result is string)
  275. {
  276. var error = (string)result;
  277. if (!string.IsNullOrEmpty(error)) response.Error(error);
  278. }
  279. }
  280. }
  281. }
  282. }
  283. catch (Exception exception)
  284. {
  285. response.Error(exception.InnerException);
  286. }
  287. // 释放控制器。
  288. RuntimeUtility.Dispose(controller);
  289. }
  290. void Output(string text, string type = "text/plain; charset=utf-8")
  291. {
  292. var bytes = TextUtility.ToBinary(text);
  293. Output(bytes, type);
  294. }
  295. void Output(byte[] bytes, string type = "application/octet-stream")
  296. {
  297. var length = bytes == null ? 0 : bytes.LongLength;
  298. SetContentType(type);
  299. SetContentLength(length);
  300. if (bytes != null && bytes.LongLength > 0L)
  301. {
  302. WriteResponse(bytes);
  303. }
  304. }
  305. void Output(Stream stream, string type = "application/octet-stream")
  306. {
  307. SetContentType(type);
  308. if (stream != null)
  309. {
  310. var length = stream.Length - stream.Position;
  311. SetContentLength(length);
  312. if (length > 0 && stream.CanRead) WriteResponse(stream);
  313. RuntimeUtility.Dispose(stream);
  314. }
  315. }
  316. // 输出 ApiResponse,返回错误信息。
  317. string Output(ApiResponse apiResponse)
  318. {
  319. // Ticket。
  320. if (ApiRequest.Method == HttpMethod.GET)
  321. {
  322. if (apiResponse.Ticket != null && !apiResponse.Cookies.HasKey("ticket", true))
  323. {
  324. apiResponse.Cookies.Add("ticket", apiResponse.Ticket);
  325. }
  326. }
  327. // 设置自定义头。
  328. AddHeaders(apiResponse.Headers);
  329. var setCookies = SetCookies(apiResponse.Cookies);
  330. if (setCookies.NotEmpty())
  331. {
  332. apiResponse.Error("系统错误。");
  333. apiResponse.Data.Reset(Json.NewObject());
  334. apiResponse.Data["cookies"] = setCookies;
  335. }
  336. //
  337. if (apiResponse.Type == ApiFormat.Redirect)
  338. {
  339. SetCacheControl(0);
  340. Redirect(apiResponse.RedirectUrl);
  341. return null;
  342. }
  343. //
  344. if (apiResponse.Type == ApiFormat.Json)
  345. {
  346. SetCacheControl(apiResponse.Expires);
  347. Output(ExportJson(apiResponse));
  348. return null;
  349. }
  350. //
  351. if (apiResponse.Type == ApiFormat.Text)
  352. {
  353. SetCacheControl(apiResponse.Expires);
  354. Output(apiResponse.TextString, apiResponse.TextType);
  355. return null;
  356. }
  357. //
  358. if (apiResponse.Type == ApiFormat.Binary)
  359. {
  360. var type = apiResponse.BinaryType ?? "application/octet-stream";
  361. if (apiResponse.BinaryBytes != null)
  362. {
  363. SetCacheControl(apiResponse.Expires);
  364. Output(apiResponse.BinaryBytes, type);
  365. }
  366. else if (apiResponse.BinaryStream != null)
  367. {
  368. SetCacheControl(apiResponse.Expires);
  369. Output(apiResponse.BinaryStream, type);
  370. }
  371. else if (!string.IsNullOrEmpty(apiResponse.BinaryPath))
  372. {
  373. SetCacheControl(apiResponse.Expires);
  374. Output(StorageUtility.ReadFile(apiResponse.BinaryPath), type);
  375. }
  376. return null;
  377. }
  378. //
  379. if (apiResponse.Type == ApiFormat.File)
  380. {
  381. if (apiResponse.FileStream != null)
  382. {
  383. SetCacheControl(apiResponse.Expires);
  384. AddHeader("Content-Disposition", $"attachment; filename={TextUtility.EncodeUrl(apiResponse.FileName)}");
  385. Output(apiResponse.FileStream, apiResponse.FileType ?? "application/octet-stream");
  386. }
  387. return null;
  388. }
  389. // Context.Response.Flush();
  390. // Context.Response.End();
  391. return null;
  392. }
  393. void AddHeaders(StringPairs headers)
  394. {
  395. foreach (var header in headers) AddHeader(header.Key, header.Value);
  396. }
  397. string SetCookies(IEnumerable<KeyValuePair<string, string>> cookies)
  398. {
  399. if (cookies == null) return null;
  400. var ps = new List<string>();
  401. foreach (var kvp in cookies)
  402. {
  403. var k = kvp.Key ?? "";
  404. var v = kvp.Value ?? "";
  405. if (k.IsEmpty()) continue;
  406. ps.Add(TextUtility.Merge(k, "=", v));
  407. }
  408. if (ps.Count < 1) return null;
  409. var value = TextUtility.Join("; ", ps);
  410. return AddHeader("Set-Cookie", value);
  411. }
  412. // 从 Context 获取 ApiRequest 模型。
  413. ApiRequest GetRequest()
  414. {
  415. // 检查 URL 的 Path。
  416. // if (string.IsNullOrEmpty(request.Path)) return null;
  417. // 创建数据对象。
  418. var apiRequest = new ApiRequest();
  419. // Http Method。
  420. apiRequest.Method = Method;
  421. // 基本信息。
  422. apiRequest.Headers = GetHeaders();
  423. apiRequest.IP = GetClientIP();
  424. apiRequest.Url = Url;
  425. apiRequest.Referrer = GetReferrer();
  426. apiRequest.Parameters = WebUtility.ParseParameters(Url.Query);
  427. // Headers。
  428. apiRequest.UserAgent = WebUtility.GetUserAgent(apiRequest.Headers);
  429. apiRequest.Cookies = GetCookies();
  430. // 匹配 API。
  431. var application = null as string;
  432. var function = null as string;
  433. var random = null as string;
  434. var ticket = null as string;
  435. var session = null as string;
  436. var page = null as string;
  437. // 解析 POST 请求。
  438. if (apiRequest.Method == Apewer.Network.HttpMethod.POST)
  439. {
  440. var post = null as byte[];
  441. var length = GetRequestLength();
  442. if (length < 0 || (length > 0 && length <= MaxRequestBody))
  443. {
  444. post = ReadRequest();
  445. }
  446. // 解析 POST。
  447. if (post != null && post.Length > 1L)
  448. {
  449. // 检查 JSON 的格式,第一位必须是“{”或“[”。
  450. if (post[0] == 123 || post[0] == 91)
  451. {
  452. var text = TextUtility.FromBinary(post);
  453. var json = Json.Parse(text);
  454. if (json != null && json.IsObject)
  455. {
  456. application = json["application"];
  457. function = json["function"];
  458. random = json["random"];
  459. ticket = json["ticket"];
  460. session = json["session"];
  461. page = json["page"];
  462. var data = json.GetProperty("data");
  463. apiRequest.PostData = post;
  464. apiRequest.PostText = text;
  465. apiRequest.PostJson = json;
  466. apiRequest.Data = data ?? Json.NewObject();
  467. }
  468. }
  469. }
  470. }
  471. // 解析 URL 参数。
  472. // URL 参数的优先级应高于 URL 路径,以避免反向代理产生的路径问题。
  473. if (string.IsNullOrEmpty(application)) application = WebUtility.GetParameter(apiRequest.Parameters, "application");
  474. if (string.IsNullOrEmpty(function)) function = WebUtility.GetParameter(apiRequest.Parameters, "function");
  475. if (string.IsNullOrEmpty(random)) random = WebUtility.GetParameter(apiRequest.Parameters, "random");
  476. if (string.IsNullOrEmpty(ticket)) ticket = WebUtility.GetParameter(apiRequest.Parameters, "ticket");
  477. if (string.IsNullOrEmpty(session)) session = WebUtility.GetParameter(apiRequest.Parameters, "session");
  478. if (string.IsNullOrEmpty(page)) page = WebUtility.GetParameter(apiRequest.Parameters, "page");
  479. // 从 Cookie 中获取 Ticket。
  480. if (string.IsNullOrEmpty(ticket)) ticket = apiRequest.Cookies.GetValue("ticket");
  481. // 最后检查 URL 路径。
  482. var paths = (apiRequest.Url.AbsolutePath ?? "").Split('/');
  483. if (string.IsNullOrEmpty(application) && paths.Length >= 2) application = TextUtility.DecodeUrl(paths[1]);
  484. if (string.IsNullOrEmpty(function) && paths.Length >= 3) function = TextUtility.DecodeUrl(paths[2]);
  485. // 修正内容。
  486. application = application.SafeLower().SafeTrim();
  487. function = function.SafeLower().SafeTrim();
  488. random = random.SafeLower().SafeTrim();
  489. ticket = ticket.SafeLower().SafeTrim();
  490. session = session.SafeLower().SafeTrim();
  491. page = page.SafeTrim();
  492. // 设置请求:回传。
  493. apiRequest.Application = application;
  494. apiRequest.Function = function;
  495. apiRequest.Random = random;
  496. // 设置请求:不回传。
  497. apiRequest.Ticket = ticket;
  498. apiRequest.Session = session;
  499. apiRequest.Page = page;
  500. // 返回结果。
  501. return apiRequest;
  502. }
  503. private static string ExportJson(ApiResponse response)
  504. {
  505. if (response == null) return "{}";
  506. var json = Json.NewObject();
  507. // 执行时间。
  508. if (ApiOptions.WithClock)
  509. {
  510. json.SetProperty("clock", response.Ending.ToLucid());
  511. }
  512. // 持续时间。
  513. if (ApiOptions.WithDuration)
  514. {
  515. var seconds = Math.Floor((response.Ending - response.Beginning).TotalMilliseconds) / 1000D;
  516. json.SetProperty("duration", seconds);
  517. }
  518. // 随机值。
  519. if (response.Random.NotEmpty()) json.SetProperty("random", response.Random);
  520. // 调用。
  521. if (ApiOptions.WithTarget)
  522. {
  523. json.SetProperty("application", response.Application);
  524. json.SetProperty("function", response.Function);
  525. }
  526. // 状态。
  527. json.SetProperty("status", (TextUtility.IsBlank(response.Status) ? TextUtility.EmptyString : response.Status.ToLower()));
  528. json.SetProperty("message", response.Message);
  529. // Ticket。
  530. if (response.Ticket != null) json.SetProperty("ticket", response.Ticket);
  531. // 异常。
  532. if (ApiOptions.AllowException && response.Exception != null)
  533. {
  534. try
  535. {
  536. var exMessage = null as string;
  537. var exStackTrace = null as string;
  538. var exSource = null as string;
  539. var exHelpLink = null as string;
  540. exMessage = response.Exception.Message;
  541. exStackTrace = response.Exception.StackTrace;
  542. exSource = response.Exception.Source;
  543. exHelpLink = response.Exception.HelpLink;
  544. // Exception 对象的主要属性。
  545. var exJson = Json.NewObject();
  546. exJson.SetProperty("type", response.Exception.GetType().FullName);
  547. exJson.SetProperty("message", exMessage);
  548. exJson.SetProperty("stack", Json.Parse((exStackTrace ?? "").Replace("\r", "").Split('\n'), false));
  549. exJson.SetProperty("source", exSource);
  550. exJson.SetProperty("helplink", exHelpLink);
  551. // WebException 附加数据。
  552. var webex = response.Exception as System.Net.WebException;
  553. if (webex != null) exJson.SetProperty("data", Json.Parse(webex.Data));
  554. json.SetProperty("exception", exJson);
  555. }
  556. catch (Exception ex)
  557. {
  558. var exJson = Json.NewObject();
  559. exJson.SetProperty("message", TextUtility.Merge("设置 Exception 时再次发生异常:", ex.Message));
  560. json.SetProperty("exception", exJson);
  561. }
  562. }
  563. // 用户数据。
  564. json.SetProperty("data", response.Data);
  565. var indented = ApiOptions.JsonIndent || response.Indented;
  566. var text = json.ToString(indented);
  567. return text;
  568. }
  569. }
  570. internal sealed class ApiProcessorListener : ApiProcessor
  571. {
  572. private HttpListenerContext Context;
  573. private HttpListenerRequest Request;
  574. private HttpListenerResponse Response;
  575. /// <exception cref="ArgumentNullException"></exception>
  576. public ApiProcessorListener(HttpListenerContext context)
  577. {
  578. if (context == null) throw new ArgumentNullException(nameof(context));
  579. Context = context;
  580. Request = context.Request;
  581. Response = context.Response;
  582. HaveContext = true;
  583. }
  584. protected override object GetContext() => Context;
  585. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  586. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  587. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  588. protected override Uri GetReferrer() => Request.UrlReferrer;
  589. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  590. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  591. protected override long GetRequestLength() => Request.ContentLength64;
  592. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.InputStream);
  593. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  594. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  595. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  596. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  597. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  598. protected override void WriteResponse(byte[] bytes) => BinaryUtility.Write(Response.OutputStream, bytes);
  599. protected override void WriteResponse(Stream stream) => BinaryUtility.Read(stream, Response.OutputStream);
  600. }
  601. #if NETFX
  602. internal sealed class ApiProcessorIIS : ApiProcessor
  603. {
  604. private System.Web.HttpContext Context;
  605. private System.Web.HttpRequest Request;
  606. private System.Web.HttpResponse Response;
  607. /// <exception cref="ArgumentNullException"></exception>
  608. public ApiProcessorIIS(HttpContext context)
  609. {
  610. if (context == null) context = HttpContext.Current;
  611. if (context == null) throw new ArgumentNullException(nameof(context));
  612. Context = context;
  613. Request = context.Request;
  614. Response = context.Response;
  615. HaveContext = true;
  616. }
  617. protected override object GetContext() => Context;
  618. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  619. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  620. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  621. protected override Uri GetReferrer() => Request.UrlReferrer;
  622. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  623. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  624. protected override long GetRequestLength() => Request.ContentLength;
  625. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.InputStream);
  626. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  627. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  628. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  629. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  630. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  631. protected override void WriteResponse(byte[] bytes)
  632. {
  633. BinaryUtility.Write(Context.Response.OutputStream, bytes);
  634. }
  635. protected override void WriteResponse(Stream stream)
  636. {
  637. BinaryUtility.Read(stream, Context.Response.OutputStream);
  638. }
  639. }
  640. #endif
  641. #if NETCORE
  642. internal sealed class ApiProcessorCore : ApiProcessor
  643. {
  644. private bool AllowSynchronousIO = true;
  645. private Microsoft.AspNetCore.Http.HttpContext Context;
  646. private Microsoft.AspNetCore.Http.HttpRequest Request;
  647. private Microsoft.AspNetCore.Http.HttpResponse Response;
  648. /// <exception cref="ArgumentNullException"></exception>
  649. public ApiProcessorCore(HttpContext context)
  650. {
  651. if (context == null) throw new ArgumentNullException(nameof(context));
  652. Context = context;
  653. Request = context.Request;
  654. Response = context.Response;
  655. HaveContext = true;
  656. }
  657. protected override object GetContext() => Context;
  658. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  659. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  660. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  661. protected override Uri GetReferrer() => null;
  662. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  663. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  664. protected override long GetRequestLength() => Request.ContentLength ?? -1;
  665. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.Body);
  666. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  667. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  668. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  669. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  670. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  671. protected override void WriteResponse(byte[] bytes)
  672. {
  673. var body = Context.Response.Body;
  674. // sync
  675. BinaryUtility.Write(body, bytes);
  676. // async
  677. // body.BeginWrite(bytes, 0, bytes.Length, (ar) => body.EndWrite(ar), null);
  678. }
  679. protected override void WriteResponse(Stream stream)
  680. {
  681. var body = Context.Response.Body;
  682. // sync
  683. BinaryUtility.Read(stream, body);
  684. // async
  685. }
  686. private static void ReadAsync(Stream source, Stream destination, Func<long, bool> progress = null, int buffer = 4096, Action after = null)
  687. {
  688. if (source == null || !source.CanRead || destination == null || !destination.CanWrite)
  689. {
  690. after?.Invoke();
  691. return;
  692. }
  693. ReadAsync(source, destination, progress, buffer, after, 0L);
  694. }
  695. private static void ReadAsync(Stream source, Stream destination, Func<long, bool> progress, int buffer, Action after, long total)
  696. {
  697. // 缓冲区。
  698. var limit = buffer < 1 ? 1 : buffer;
  699. var temp = new byte[limit];
  700. // 读取一次。
  701. try
  702. {
  703. source.BeginRead(temp, 0, limit, (ar1) =>
  704. {
  705. var count = source.EndRead(ar1);
  706. if (count > 0)
  707. {
  708. destination.BeginWrite(temp, 0, count, (ar2) =>
  709. {
  710. destination.EndWrite(ar2);
  711. total += count;
  712. if (progress != null)
  713. {
  714. var @continue = progress.Invoke(total);
  715. if (!@continue)
  716. {
  717. after?.Invoke();
  718. return;
  719. }
  720. }
  721. ReadAsync(source, destination, progress, buffer, after, total);
  722. }, null);
  723. }
  724. else after?.Invoke();
  725. }, null);
  726. }
  727. catch { }
  728. }
  729. }
  730. #endif
  731. }