ServiceHandler.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Emby.Server.Implementations.HttpServer;
  7. using MediaBrowser.Model.Services;
  8. using Microsoft.Extensions.Logging;
  9. namespace Emby.Server.Implementations.Services
  10. {
  11. public class ServiceHandler
  12. {
  13. public RestPath RestPath { get; }
  14. public string ResponseContentType { get; }
  15. internal ServiceHandler(RestPath restPath, string responseContentType)
  16. {
  17. RestPath = restPath;
  18. ResponseContentType = responseContentType;
  19. }
  20. protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
  21. {
  22. if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
  23. {
  24. var deserializer = RequestHelper.GetRequestReader(host, contentType);
  25. return deserializer?.Invoke(requestType, httpReq.InputStream);
  26. }
  27. return Task.FromResult(host.CreateInstance(requestType));
  28. }
  29. public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
  30. {
  31. contentType = null;
  32. var pos = pathInfo.LastIndexOf('.');
  33. if (pos != -1)
  34. {
  35. var format = pathInfo.Substring(pos + 1);
  36. contentType = GetFormatContentType(format);
  37. if (contentType != null)
  38. {
  39. pathInfo = pathInfo.Substring(0, pos);
  40. }
  41. }
  42. return pathInfo;
  43. }
  44. private static string GetFormatContentType(string format)
  45. {
  46. //built-in formats
  47. switch (format)
  48. {
  49. case "json": return "application/json";
  50. case "xml": return "application/xml";
  51. default: return null;
  52. }
  53. }
  54. public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, IResponse httpRes, ILogger logger, CancellationToken cancellationToken)
  55. {
  56. httpReq.Items["__route"] = RestPath;
  57. if (ResponseContentType != null)
  58. {
  59. httpReq.ResponseContentType = ResponseContentType;
  60. }
  61. var request = httpReq.Dto = await CreateRequest(httpHost, httpReq, RestPath, logger).ConfigureAwait(false);
  62. httpHost.ApplyRequestFilters(httpReq, httpRes, request);
  63. var response = await httpHost.ServiceController.Execute(httpHost, request, httpReq).ConfigureAwait(false);
  64. // Apply response filters
  65. foreach (var responseFilter in httpHost.ResponseFilters)
  66. {
  67. responseFilter(httpReq, httpRes, response);
  68. }
  69. await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
  70. }
  71. public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
  72. {
  73. var requestType = restPath.RequestType;
  74. if (RequireqRequestStream(requestType))
  75. {
  76. // Used by IRequiresRequestStream
  77. var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
  78. var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
  79. var rawReq = (IRequiresRequestStream)request;
  80. rawReq.RequestStream = httpReq.InputStream;
  81. return rawReq;
  82. }
  83. else
  84. {
  85. var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
  86. var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
  87. return CreateRequest(httpReq, restPath, requestParams, requestDto);
  88. }
  89. }
  90. public static bool RequireqRequestStream(Type requestType)
  91. {
  92. var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
  93. return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
  94. }
  95. public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
  96. {
  97. var pathInfo = !restPath.IsWildCardPath
  98. ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
  99. : httpReq.PathInfo;
  100. return restPath.CreateRequest(pathInfo, requestParams, requestDto);
  101. }
  102. /// <summary>
  103. /// Duplicate Params are given a unique key by appending a #1 suffix
  104. /// </summary>
  105. private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
  106. {
  107. var map = new Dictionary<string, string>();
  108. foreach (var name in request.QueryString.Keys)
  109. {
  110. if (name == null)
  111. {
  112. // thank you ASP.NET
  113. continue;
  114. }
  115. var values = request.QueryString[name];
  116. if (values.Count == 1)
  117. {
  118. map[name] = values[0];
  119. }
  120. else
  121. {
  122. for (var i = 0; i < values.Count; i++)
  123. {
  124. map[name + (i == 0 ? "" : "#" + i)] = values[i];
  125. }
  126. }
  127. }
  128. if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
  129. {
  130. var formData = await request.GetFormData().ConfigureAwait(false);
  131. if (formData != null)
  132. {
  133. foreach (var name in formData.Keys)
  134. {
  135. if (name == null)
  136. {
  137. // thank you ASP.NET
  138. continue;
  139. }
  140. var values = formData.GetValues(name);
  141. if (values.Count == 1)
  142. {
  143. map[name] = values[0];
  144. }
  145. else
  146. {
  147. for (var i = 0; i < values.Count; i++)
  148. {
  149. map[name + (i == 0 ? "" : "#" + i)] = values[i];
  150. }
  151. }
  152. }
  153. }
  154. }
  155. return map;
  156. }
  157. private static bool IsMethod(string method, string expected)
  158. {
  159. return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
  160. }
  161. /// <summary>
  162. /// Duplicate params have their values joined together in a comma-delimited string
  163. /// </summary>
  164. private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
  165. {
  166. var map = new Dictionary<string, string>();
  167. foreach (var name in request.QueryString.Keys)
  168. {
  169. if (name == null)
  170. {
  171. // thank you ASP.NET
  172. continue;
  173. }
  174. map[name] = request.QueryString[name];
  175. }
  176. if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
  177. {
  178. var formData = await request.GetFormData().ConfigureAwait(false);
  179. if (formData != null)
  180. {
  181. foreach (var name in formData.Keys)
  182. {
  183. if (name == null)
  184. {
  185. // thank you ASP.NET
  186. continue;
  187. }
  188. map[name] = formData[name];
  189. }
  190. }
  191. }
  192. return map;
  193. }
  194. }
  195. }