2017-02-13 01:07:48 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Reflection;
|
2017-05-22 04:54:02 +00:00
|
|
|
using System.Threading;
|
2017-02-13 01:07:48 +00:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Emby.Server.Implementations.HttpServer;
|
|
|
|
using MediaBrowser.Model.Services;
|
2019-01-13 19:22:24 +00:00
|
|
|
using Microsoft.Extensions.Logging;
|
2017-02-13 01:07:48 +00:00
|
|
|
|
|
|
|
namespace Emby.Server.Implementations.Services
|
|
|
|
{
|
|
|
|
public class ServiceHandler
|
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
public RestPath RestPath { get; }
|
|
|
|
|
|
|
|
public string ResponseContentType { get; }
|
|
|
|
|
|
|
|
internal ServiceHandler(RestPath restPath, string responseContentType)
|
|
|
|
{
|
|
|
|
RestPath = restPath;
|
|
|
|
ResponseContentType = responseContentType;
|
|
|
|
}
|
|
|
|
|
2018-09-12 17:26:21 +00:00
|
|
|
protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
|
|
|
|
{
|
2017-02-13 02:06:54 +00:00
|
|
|
var deserializer = RequestHelper.GetRequestReader(host, contentType);
|
2019-03-27 11:43:46 +00:00
|
|
|
return deserializer?.Invoke(requestType, httpReq.InputStream);
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
2019-03-26 18:20:40 +00:00
|
|
|
|
2019-01-07 23:27:46 +00:00
|
|
|
return Task.FromResult(host.CreateInstance(requestType));
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
|
|
|
|
{
|
|
|
|
contentType = null;
|
|
|
|
var pos = pathInfo.LastIndexOf('.');
|
2019-03-26 18:20:40 +00:00
|
|
|
if (pos != -1)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
var format = pathInfo.Substring(pos + 1);
|
|
|
|
contentType = GetFormatContentType(format);
|
|
|
|
if (contentType != null)
|
|
|
|
{
|
|
|
|
pathInfo = pathInfo.Substring(0, pos);
|
|
|
|
}
|
|
|
|
}
|
2019-03-26 18:20:40 +00:00
|
|
|
|
2017-02-13 01:07:48 +00:00
|
|
|
return pathInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static string GetFormatContentType(string format)
|
|
|
|
{
|
|
|
|
//built-in formats
|
2019-03-26 18:20:40 +00:00
|
|
|
switch (format)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
case "json": return "application/json";
|
|
|
|
case "xml": return "application/xml";
|
|
|
|
default: return null;
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-26 18:20:40 +00:00
|
|
|
public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, IResponse httpRes, ILogger logger, CancellationToken cancellationToken)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
httpReq.Items["__route"] = RestPath;
|
2017-02-13 01:07:48 +00:00
|
|
|
|
|
|
|
if (ResponseContentType != null)
|
2019-03-26 18:20:40 +00:00
|
|
|
{
|
2017-02-13 01:07:48 +00:00
|
|
|
httpReq.ResponseContentType = ResponseContentType;
|
2019-03-26 18:20:40 +00:00
|
|
|
}
|
2017-02-13 01:07:48 +00:00
|
|
|
|
2019-03-26 18:20:40 +00:00
|
|
|
var request = httpReq.Dto = await CreateRequest(httpHost, httpReq, RestPath, logger).ConfigureAwait(false);
|
2017-02-13 01:07:48 +00:00
|
|
|
|
2019-03-26 18:20:40 +00:00
|
|
|
httpHost.ApplyRequestFilters(httpReq, httpRes, request);
|
2017-02-13 01:07:48 +00:00
|
|
|
|
2019-03-26 18:20:40 +00:00
|
|
|
var response = await httpHost.ServiceController.Execute(httpHost, request, httpReq).ConfigureAwait(false);
|
2017-02-13 01:07:48 +00:00
|
|
|
|
|
|
|
// Apply response filters
|
2019-03-26 18:20:40 +00:00
|
|
|
foreach (var responseFilter in httpHost.ResponseFilters)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
responseFilter(httpReq, httpRes, response);
|
|
|
|
}
|
|
|
|
|
2017-05-22 04:54:02 +00:00
|
|
|
await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
|
2018-09-12 17:26:21 +00:00
|
|
|
public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
var requestType = restPath.RequestType;
|
|
|
|
|
|
|
|
if (RequireqRequestStream(requestType))
|
|
|
|
{
|
|
|
|
// Used by IRequiresRequestStream
|
2018-09-12 17:26:21 +00:00
|
|
|
var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
|
|
|
|
var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
|
2017-09-03 07:28:58 +00:00
|
|
|
|
|
|
|
var rawReq = (IRequiresRequestStream)request;
|
|
|
|
rawReq.RequestStream = httpReq.InputStream;
|
|
|
|
return rawReq;
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
2018-09-12 17:26:21 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
|
2017-02-13 01:07:48 +00:00
|
|
|
|
2018-09-12 17:26:21 +00:00
|
|
|
var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
|
|
|
|
|
|
|
|
return CreateRequest(httpReq, restPath, requestParams, requestDto);
|
|
|
|
}
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
|
2018-09-12 17:26:21 +00:00
|
|
|
public static bool RequireqRequestStream(Type requestType)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
|
|
|
|
|
|
|
|
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
|
|
|
|
{
|
|
|
|
var pathInfo = !restPath.IsWildCardPath
|
2019-01-17 17:47:41 +00:00
|
|
|
? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
|
2017-02-13 01:07:48 +00:00
|
|
|
: httpReq.PathInfo;
|
|
|
|
|
|
|
|
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Duplicate Params are given a unique key by appending a #1 suffix
|
|
|
|
/// </summary>
|
2018-09-12 17:26:21 +00:00
|
|
|
private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
var map = new Dictionary<string, string>();
|
|
|
|
|
|
|
|
foreach (var name in request.QueryString.Keys)
|
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
if (name == null)
|
|
|
|
{
|
|
|
|
// thank you ASP.NET
|
|
|
|
continue;
|
|
|
|
}
|
2017-02-13 01:07:48 +00:00
|
|
|
|
2019-02-27 13:23:39 +00:00
|
|
|
var values = request.QueryString[name];
|
2017-08-24 19:52:19 +00:00
|
|
|
if (values.Count == 1)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
map[name] = values[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-24 19:52:19 +00:00
|
|
|
for (var i = 0; i < values.Count; i++)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-03 07:28:58 +00:00
|
|
|
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2018-09-12 17:26:21 +00:00
|
|
|
var formData = await request.GetFormData().ConfigureAwait(false);
|
2017-09-03 07:28:58 +00:00
|
|
|
if (formData != null)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2017-09-03 07:28:58 +00:00
|
|
|
foreach (var name in formData.Keys)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
if (name == null)
|
|
|
|
{
|
|
|
|
// thank you ASP.NET
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-03 07:28:58 +00:00
|
|
|
|
|
|
|
var values = formData.GetValues(name);
|
|
|
|
if (values.Count == 1)
|
|
|
|
{
|
|
|
|
map[name] = values[0];
|
|
|
|
}
|
|
|
|
else
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2017-09-03 07:28:58 +00:00
|
|
|
for (var i = 0; i < values.Count; i++)
|
|
|
|
{
|
|
|
|
map[name + (i == 0 ? "" : "#" + i)] = values[i];
|
|
|
|
}
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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>
|
2018-09-12 17:26:21 +00:00
|
|
|
private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
|
|
|
var map = new Dictionary<string, string>();
|
|
|
|
|
|
|
|
foreach (var name in request.QueryString.Keys)
|
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
if (name == null)
|
|
|
|
{
|
|
|
|
// thank you ASP.NET
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-02-13 01:07:48 +00:00
|
|
|
map[name] = request.QueryString[name];
|
|
|
|
}
|
|
|
|
|
2017-09-03 07:28:58 +00:00
|
|
|
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2018-09-12 17:26:21 +00:00
|
|
|
var formData = await request.GetFormData().ConfigureAwait(false);
|
2017-09-03 07:28:58 +00:00
|
|
|
if (formData != null)
|
2017-02-13 01:07:48 +00:00
|
|
|
{
|
2017-09-03 07:28:58 +00:00
|
|
|
foreach (var name in formData.Keys)
|
|
|
|
{
|
2019-03-26 18:20:40 +00:00
|
|
|
if (name == null)
|
|
|
|
{
|
|
|
|
// thank you ASP.NET
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-09-03 07:28:58 +00:00
|
|
|
map[name] = formData[name];
|
|
|
|
}
|
2017-02-13 01:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|