jellyfin/Emby.Server.Implementations/Services/ServiceHandler.cs

213 lines
7.4 KiB
C#
Raw Normal View History

#pragma warning disable CS1591
2017-02-13 01:07:48 +00:00
using System;
using System.Collections.Generic;
2020-07-22 11:52:31 +00:00
using System.Net.Mime;
2017-02-13 01:07:48 +00:00
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;
2020-07-18 15:54:23 +00:00
using MediaBrowser.Common.Extensions;
2017-02-13 01:07:48 +00:00
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
2017-02-13 01:07:48 +00:00
namespace Emby.Server.Implementations.Services
{
public class ServiceHandler
{
private RestPath _restPath;
2019-03-26 18:20:40 +00:00
private string _responseContentType;
2019-03-26 18:20:40 +00:00
internal ServiceHandler(RestPath restPath, string responseContentType)
{
_restPath = restPath;
_responseContentType = responseContentType;
2019-03-26 18:20:40 +00:00
}
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);
if (deserializer != null)
{
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.AsSpan().Slice(pos + 1);
2017-02-13 01:07:48 +00:00
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(ReadOnlySpan<char> format)
2017-02-13 01:07:48 +00:00
{
if (format.Equals("json", StringComparison.Ordinal))
2017-02-13 01:07:48 +00:00
{
2020-07-22 11:52:31 +00:00
return MediaTypeNames.Application.Json;
2017-02-13 01:07:48 +00:00
}
else if (format.Equals("xml", StringComparison.Ordinal))
{
2020-07-22 11:52:31 +00:00
return MediaTypeNames.Application.Xml;
}
return null;
2017-02-13 01:07:48 +00:00
}
public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken)
2017-02-13 01:07:48 +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
{
httpReq.ResponseContentType = _responseContentType;
2019-03-26 18:20:40 +00:00
}
2017-02-13 01:07:48 +00:00
var request = 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);
2020-07-17 11:56:52 +00:00
2020-07-17 15:06:32 +00:00
httpRes.HttpContext.SetServiceStackRequest(httpReq);
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
var requestParams = GetRequestParams(httpReq.Response.HttpContext.Request);
2018-09-12 17:26:21 +00:00
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 = GetFlattenedRequestParams(httpReq.Response.HttpContext.Request);
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-08-09 20:37:44 +00:00
? GetSanitizedPathInfo(httpReq.PathInfo, out _)
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>
private static Dictionary<string, string> GetRequestParams(HttpRequest request)
2017-02-13 01:07:48 +00:00
{
var map = new Dictionary<string, string>();
foreach (var pair in request.Query)
2017-02-13 01:07:48 +00:00
{
var values = pair.Value;
2017-08-24 19:52:19 +00:00
if (values.Count == 1)
2017-02-13 01:07:48 +00:00
{
map[pair.Key] = values[0];
2017-02-13 01:07:48 +00:00
}
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
{
2019-08-09 20:37:44 +00:00
map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
2017-02-13 01:07:48 +00:00
}
}
}
2019-08-09 20:37:44 +00:00
if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
&& request.HasFormContentType)
2017-02-13 01:07:48 +00:00
{
foreach (var pair in request.Form)
2017-02-13 01:07:48 +00:00
{
var values = pair.Value;
if (values.Count == 1)
2017-02-13 01:07:48 +00:00
{
map[pair.Key] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
2017-02-13 01:07:48 +00:00
{
2019-08-09 20:37:44 +00:00
map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
2017-02-13 01:07:48 +00:00
}
}
}
}
return map;
}
private static bool IsMethod(string method, string expected)
2019-08-09 20:37:44 +00:00
=> string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
2017-02-13 01:07:48 +00:00
/// <summary>
/// Duplicate params have their values joined together in a comma-delimited string.
2017-02-13 01:07:48 +00:00
/// </summary>
private static Dictionary<string, string> GetFlattenedRequestParams(HttpRequest request)
2017-02-13 01:07:48 +00:00
{
var map = new Dictionary<string, string>();
foreach (var pair in request.Query)
2017-02-13 01:07:48 +00:00
{
map[pair.Key] = pair.Value;
2017-02-13 01:07:48 +00:00
}
2019-08-09 20:37:44 +00:00
if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
&& request.HasFormContentType)
2017-02-13 01:07:48 +00:00
{
foreach (var pair in request.Form)
2017-02-13 01:07:48 +00:00
{
map[pair.Key] = pair.Value;
2017-02-13 01:07:48 +00:00
}
}
return map;
}
}
}