Merge branch 'master' of https://github.com/MediaBrowser/Emby
commit
a73eeac686
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
|
||||
namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
public class RequestHelper
|
||||
{
|
||||
public static Func<Type, Stream, object> GetRequestReader(HttpListenerHost host, string contentType)
|
||||
{
|
||||
switch (GetContentTypeWithoutEncoding(contentType))
|
||||
{
|
||||
case "application/xml":
|
||||
case "text/xml":
|
||||
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
|
||||
return host.DeserializeXml;
|
||||
|
||||
case "application/json":
|
||||
case "text/json":
|
||||
return host.DeserializeJson;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Action<object, Stream> GetResponseWriter(HttpListenerHost host, string contentType)
|
||||
{
|
||||
switch (GetContentTypeWithoutEncoding(contentType))
|
||||
{
|
||||
case "application/xml":
|
||||
case "text/xml":
|
||||
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
|
||||
return host.SerializeToXml;
|
||||
|
||||
case "application/json":
|
||||
case "text/json":
|
||||
return host.SerializeToJson;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetContentTypeWithoutEncoding(string contentType)
|
||||
{
|
||||
return contentType == null
|
||||
? null
|
||||
: contentType.Split(';')[0].ToLower().Trim();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
||||
namespace Emby.Server.Implementations.Services
|
||||
{
|
||||
public class ServiceHandler
|
||||
{
|
||||
public async Task<object> HandleResponseAsync(object response)
|
||||
{
|
||||
var taskResponse = response as Task;
|
||||
|
||||
if (taskResponse == null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
await taskResponse.ConfigureAwait(false);
|
||||
|
||||
var taskResult = GetTaskResult(taskResponse);
|
||||
|
||||
var subTask = taskResult as Task;
|
||||
if (subTask != null)
|
||||
{
|
||||
taskResult = GetTaskResult(subTask);
|
||||
}
|
||||
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
internal static object GetTaskResult(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
var taskObject = task as Task<object>;
|
||||
if (taskObject != null)
|
||||
{
|
||||
return taskObject.Result;
|
||||
}
|
||||
|
||||
task.Wait();
|
||||
|
||||
var type = task.GetType().GetTypeInfo();
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return type.GetDeclaredProperty("Result").GetValue(task);
|
||||
}
|
||||
catch (TypeAccessException)
|
||||
{
|
||||
return null; //return null for void Task's
|
||||
}
|
||||
}
|
||||
|
||||
protected static object CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
|
||||
{
|
||||
var deserializer = RequestHelper.GetRequestReader(host, contentType);
|
||||
if (deserializer != null)
|
||||
{
|
||||
return deserializer(requestType, httpReq.InputStream);
|
||||
}
|
||||
}
|
||||
return host.CreateInstance(requestType);
|
||||
}
|
||||
|
||||
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
|
||||
{
|
||||
pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
|
||||
|
||||
return ServiceController.Instance.GetRestPathForRequest(httpMethod, pathInfo, logger);
|
||||
}
|
||||
|
||||
public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
|
||||
{
|
||||
contentType = null;
|
||||
var pos = pathInfo.LastIndexOf('.');
|
||||
if (pos >= 0)
|
||||
{
|
||||
var format = pathInfo.Substring(pos + 1);
|
||||
contentType = GetFormatContentType(format);
|
||||
if (contentType != null)
|
||||
{
|
||||
pathInfo = pathInfo.Substring(0, pos);
|
||||
}
|
||||
}
|
||||
return pathInfo;
|
||||
}
|
||||
|
||||
private static string GetFormatContentType(string format)
|
||||
{
|
||||
//built-in formats
|
||||
if (format == "json")
|
||||
return "application/json";
|
||||
if (format == "xml")
|
||||
return "application/xml";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public RestPath GetRestPath(string httpMethod, string pathInfo)
|
||||
{
|
||||
if (this.RestPath == null)
|
||||
{
|
||||
string contentType;
|
||||
this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType);
|
||||
|
||||
if (contentType != null)
|
||||
ResponseContentType = contentType;
|
||||
}
|
||||
return this.RestPath;
|
||||
}
|
||||
|
||||
public RestPath RestPath { get; set; }
|
||||
|
||||
// Set from SSHHF.GetHandlerForPathInfo()
|
||||
public string ResponseContentType { get; set; }
|
||||
|
||||
public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName)
|
||||
{
|
||||
var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
|
||||
if (restPath == null)
|
||||
{
|
||||
throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
|
||||
}
|
||||
|
||||
SetRoute(httpReq, restPath);
|
||||
|
||||
if (ResponseContentType != null)
|
||||
httpReq.ResponseContentType = ResponseContentType;
|
||||
|
||||
var request = httpReq.Dto = CreateRequest(appHost, httpReq, restPath, logger);
|
||||
|
||||
appHost.ApplyRequestFilters(httpReq, httpRes, request);
|
||||
|
||||
var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
|
||||
|
||||
var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
|
||||
|
||||
// Apply response filters
|
||||
foreach (var responseFilter in appHost.ResponseFilters)
|
||||
{
|
||||
responseFilter(httpReq, httpRes, response);
|
||||
}
|
||||
|
||||
await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
|
||||
{
|
||||
var requestType = restPath.RequestType;
|
||||
|
||||
if (RequireqRequestStream(requestType))
|
||||
{
|
||||
// Used by IRequiresRequestStream
|
||||
return CreateRequiresRequestStreamRequest(host, httpReq, requestType);
|
||||
}
|
||||
|
||||
var requestParams = GetFlattenedRequestParams(httpReq);
|
||||
return CreateRequest(host, httpReq, restPath, requestParams);
|
||||
}
|
||||
|
||||
private static bool RequireqRequestStream(Type requestType)
|
||||
{
|
||||
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
|
||||
|
||||
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
|
||||
}
|
||||
|
||||
private static IRequiresRequestStream CreateRequiresRequestStreamRequest(HttpListenerHost host, IRequest req, Type requestType)
|
||||
{
|
||||
var restPath = GetRoute(req);
|
||||
var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), host.CreateInstance(requestType));
|
||||
|
||||
var rawReq = (IRequiresRequestStream)request;
|
||||
rawReq.RequestStream = req.InputStream;
|
||||
return rawReq;
|
||||
}
|
||||
|
||||
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
|
||||
{
|
||||
var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType);
|
||||
|
||||
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
||||
}
|
||||
|
||||
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
|
||||
{
|
||||
string contentType;
|
||||
var pathInfo = !restPath.IsWildCardPath
|
||||
? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
|
||||
: httpReq.PathInfo;
|
||||
|
||||
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate Params are given a unique key by appending a #1 suffix
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> GetRequestParams(IRequest request)
|
||||
{
|
||||
var map = new Dictionary<string, string>();
|
||||
|
||||
foreach (var name in request.QueryString.Keys)
|
||||
{
|
||||
if (name == null) continue; //thank you ASP.NET
|
||||
|
||||
var values = request.QueryString.GetValues(name);
|
||||
if (values.Length == 1)
|
||||
{
|
||||
map[name] = values[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
|
||||
{
|
||||
foreach (var name in request.FormData.Keys)
|
||||
{
|
||||
if (name == null) continue; //thank you ASP.NET
|
||||
|
||||
var values = request.FormData.GetValues(name);
|
||||
if (values.Length == 1)
|
||||
{
|
||||
map[name] = values[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static bool IsMethod(string method, string expected)
|
||||
{
|
||||
return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate params have their values joined together in a comma-delimited string
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> GetFlattenedRequestParams(IRequest request)
|
||||
{
|
||||
var map = new Dictionary<string, string>();
|
||||
|
||||
foreach (var name in request.QueryString.Keys)
|
||||
{
|
||||
if (name == null) continue; //thank you ASP.NET
|
||||
map[name] = request.QueryString[name];
|
||||
}
|
||||
|
||||
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
|
||||
{
|
||||
foreach (var name in request.FormData.Keys)
|
||||
{
|
||||
if (name == null) continue; //thank you ASP.NET
|
||||
map[name] = request.FormData[name];
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static void SetRoute(IRequest req, RestPath route)
|
||||
{
|
||||
req.Items["__route"] = route;
|
||||
}
|
||||
|
||||
private static RestPath GetRoute(IRequest req)
|
||||
{
|
||||
object route;
|
||||
req.Items.TryGetValue("__route", out route);
|
||||
return route as RestPath;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Emby.XmlTv" version="1.0.6" targetFramework="portable45-net45+win8" />
|
||||
<package id="Emby.XmlTv" version="1.0.7" targetFramework="portable45-net45+win8" />
|
||||
<package id="MediaBrowser.Naming" version="1.0.4" targetFramework="portable45-net45+win8" />
|
||||
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="portable45-net45+win8" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="portable45-net45+win8" />
|
||||
<package id="UniversalDetector" version="1.0.1" targetFramework="portable45-net45+win8" />
|
||||
</packages>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue