Advertisement
Guest User

Untitled

a guest
Feb 29th, 2020
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.86 KB | None | 0 0
  1. #pragma warning disable CS1591
  2. #pragma warning disable SA1600
  3.  
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Net.Sockets;
  10. using System.Reflection;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using Emby.Server.Implementations.Net;
  14. using Emby.Server.Implementations.Services;
  15. using MediaBrowser.Common.Extensions;
  16. using MediaBrowser.Common.Net;
  17. using MediaBrowser.Controller;
  18. using MediaBrowser.Controller.Configuration;
  19. using MediaBrowser.Controller.Net;
  20. using MediaBrowser.Model.Events;
  21. using MediaBrowser.Model.Serialization;
  22. using MediaBrowser.Model.Services;
  23. using Microsoft.AspNetCore.Http;
  24. using Microsoft.AspNetCore.WebUtilities;
  25. using Microsoft.Extensions.Configuration;
  26. using Microsoft.Extensions.Logging;
  27. using ServiceStack.Text.Jsv;
  28.  
  29. namespace Emby.Server.Implementations.HttpServer
  30. {
  31. public class HttpListenerHost : IHttpServer, IDisposable
  32. {
  33. private readonly ILogger _logger;
  34. private readonly IServerConfigurationManager _config;
  35. private readonly INetworkManager _networkManager;
  36. private readonly IServerApplicationHost _appHost;
  37. private readonly IJsonSerializer _jsonSerializer;
  38. private readonly IXmlSerializer _xmlSerializer;
  39. private readonly IHttpListener _socketListener;
  40. private readonly Func<Type, Func<string, object>> _funcParseFn;
  41. private readonly string _defaultRedirectPath;
  42. private readonly string _baseUrlPrefix;
  43. private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
  44. private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
  45. private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
  46. private bool _disposed = false;
  47.  
  48. public HttpListenerHost(
  49. IServerApplicationHost applicationHost,
  50. ILogger<HttpListenerHost> logger,
  51. IServerConfigurationManager config,
  52. IConfiguration configuration,
  53. INetworkManager networkManager,
  54. IJsonSerializer jsonSerializer,
  55. IXmlSerializer xmlSerializer,
  56. IHttpListener socketListener)
  57. {
  58. _appHost = applicationHost;
  59. _logger = logger;
  60. _config = config;
  61. _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
  62. _baseUrlPrefix = _config.Configuration.BaseUrl;
  63. _networkManager = networkManager;
  64. _jsonSerializer = jsonSerializer;
  65. _xmlSerializer = xmlSerializer;
  66. _socketListener = socketListener;
  67. _socketListener.WebSocketConnected = OnWebSocketConnected;
  68.  
  69. _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
  70.  
  71. Instance = this;
  72. ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
  73. }
  74.  
  75. public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; }
  76.  
  77. public static HttpListenerHost Instance { get; protected set; }
  78.  
  79. public string[] UrlPrefixes { get; private set; }
  80.  
  81. public string GlobalResponse { get; set; }
  82.  
  83. public ServiceController ServiceController { get; private set; }
  84.  
  85. public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
  86.  
  87. public object CreateInstance(Type type)
  88. {
  89. return _appHost.CreateInstance(type);
  90. }
  91.  
  92. private static string NormalizeUrlPath(string path)
  93. {
  94. if (path.StartsWith("/"))
  95. {
  96. // If the path begins with a leading slash, just return it as-is
  97. return path;
  98. }
  99. else
  100. {
  101. // If the path does not begin with a leading slash, append one for consistency
  102. return "/" + path;
  103. }
  104. }
  105.  
  106. /// <summary>
  107. /// Applies the request filters. Returns whether or not the request has been handled
  108. /// and no more processing should be done.
  109. /// </summary>
  110. /// <returns></returns>
  111. public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
  112. {
  113. // Exec all RequestFilter attributes with Priority < 0
  114. var attributes = GetRequestFilterAttributes(requestDto.GetType());
  115.  
  116. int count = attributes.Count;
  117. int i = 0;
  118. for (; i < count && attributes[i].Priority < 0; i++)
  119. {
  120. var attribute = attributes[i];
  121. attribute.RequestFilter(req, res, requestDto);
  122. }
  123.  
  124. // Exec remaining RequestFilter attributes with Priority >= 0
  125. for (; i < count && attributes[i].Priority >= 0; i++)
  126. {
  127. var attribute = attributes[i];
  128. attribute.RequestFilter(req, res, requestDto);
  129. }
  130. }
  131.  
  132. public Type GetServiceTypeByRequest(Type requestType)
  133. {
  134. ServiceOperationsMap.TryGetValue(requestType, out var serviceType);
  135. return serviceType;
  136. }
  137.  
  138. public void AddServiceInfo(Type serviceType, Type requestType)
  139. {
  140. ServiceOperationsMap[requestType] = serviceType;
  141. }
  142.  
  143. private List<IHasRequestFilter> GetRequestFilterAttributes(Type requestDtoType)
  144. {
  145. var attributes = requestDtoType.GetCustomAttributes(true).OfType<IHasRequestFilter>().ToList();
  146.  
  147. var serviceType = GetServiceTypeByRequest(requestDtoType);
  148. if (serviceType != null)
  149. {
  150. attributes.AddRange(serviceType.GetCustomAttributes(true).OfType<IHasRequestFilter>());
  151. }
  152.  
  153. attributes.Sort((x, y) => x.Priority - y.Priority);
  154.  
  155. return attributes;
  156. }
  157.  
  158. private void OnWebSocketConnected(WebSocketConnectEventArgs e)
  159. {
  160. if (_disposed)
  161. {
  162. return;
  163. }
  164.  
  165. var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger)
  166. {
  167. OnReceive = ProcessWebSocketMessageReceived,
  168. Url = e.Url,
  169. QueryString = e.QueryString
  170. };
  171.  
  172. connection.Closed += OnConnectionClosed;
  173.  
  174. lock (_webSocketConnections)
  175. {
  176. _webSocketConnections.Add(connection);
  177. }
  178.  
  179. WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection));
  180. }
  181.  
  182. private void OnConnectionClosed(object sender, EventArgs e)
  183. {
  184. lock (_webSocketConnections)
  185. {
  186. _webSocketConnections.Remove((IWebSocketConnection)sender);
  187. }
  188. }
  189.  
  190. private static Exception GetActualException(Exception ex)
  191. {
  192. if (ex is AggregateException agg)
  193. {
  194. var inner = agg.InnerException;
  195. if (inner != null)
  196. {
  197. return GetActualException(inner);
  198. }
  199. else
  200. {
  201. var inners = agg.InnerExceptions;
  202. if (inners != null && inners.Count > 0)
  203. {
  204. return GetActualException(inners[0]);
  205. }
  206. }
  207. }
  208.  
  209. return ex;
  210. }
  211.  
  212. private int GetStatusCode(Exception ex)
  213. {
  214. switch (ex)
  215. {
  216. case ArgumentException _: return 400;
  217. case SecurityException _: return 401;
  218. case DirectoryNotFoundException _:
  219. case FileNotFoundException _:
  220. case ResourceNotFoundException _: return 404;
  221. case MethodNotAllowedException _: return 405;
  222. default: return 500;
  223. }
  224. }
  225.  
  226. private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace)
  227. {
  228. try
  229. {
  230. ex = GetActualException(ex);
  231.  
  232. if (logExceptionStackTrace)
  233. {
  234. _logger.LogError(ex, "Error processing request");
  235. }
  236. else
  237. {
  238. _logger.LogError("Error processing request: {Message}", ex.Message);
  239. }
  240.  
  241. var httpRes = httpReq.Response;
  242.  
  243. if (httpRes.HasStarted)
  244. {
  245. return;
  246. }
  247.  
  248. var statusCode = GetStatusCode(ex);
  249. httpRes.StatusCode = statusCode;
  250.  
  251. var errContent = NormalizeExceptionMessage(ex.Message);
  252. httpRes.ContentType = "text/plain";
  253. httpRes.ContentLength = errContent.Length;
  254. await httpRes.WriteAsync(errContent).ConfigureAwait(false);
  255. }
  256. catch (Exception errorEx)
  257. {
  258. _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
  259. }
  260. }
  261.  
  262. private string NormalizeExceptionMessage(string msg)
  263. {
  264. if (msg == null)
  265. {
  266. return string.Empty;
  267. }
  268.  
  269. // Strip any information we don't want to reveal
  270.  
  271. msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase);
  272. msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
  273.  
  274. return msg;
  275. }
  276.  
  277. /// <summary>
  278. /// Shut down the Web Service
  279. /// </summary>
  280. public void Stop()
  281. {
  282. List<IWebSocketConnection> connections;
  283.  
  284. lock (_webSocketConnections)
  285. {
  286. connections = _webSocketConnections.ToList();
  287. _webSocketConnections.Clear();
  288. }
  289.  
  290. foreach (var connection in connections)
  291. {
  292. try
  293. {
  294. connection.Dispose();
  295. }
  296. catch (Exception ex)
  297. {
  298. _logger.LogError(ex, "Error disposing connection");
  299. }
  300. }
  301. }
  302.  
  303. public static string RemoveQueryStringByKey(string url, string key)
  304. {
  305. var uri = new Uri(url);
  306.  
  307. // this gets all the query string key value pairs as a collection
  308. var newQueryString = QueryHelpers.ParseQuery(uri.Query);
  309.  
  310. var originalCount = newQueryString.Count;
  311.  
  312. if (originalCount == 0)
  313. {
  314. return url;
  315. }
  316.  
  317. // this removes the key if exists
  318. newQueryString.Remove(key);
  319.  
  320. if (originalCount == newQueryString.Count)
  321. {
  322. return url;
  323. }
  324.  
  325. // this gets the page path from root without QueryString
  326. string pagePathWithoutQueryString = url.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0];
  327.  
  328. return newQueryString.Count > 0
  329. ? QueryHelpers.AddQueryString(pagePathWithoutQueryString, newQueryString.ToDictionary(kv => kv.Key, kv => kv.Value.ToString()))
  330. : pagePathWithoutQueryString;
  331. }
  332.  
  333. private static string GetUrlToLog(string url)
  334. {
  335. url = RemoveQueryStringByKey(url, "api_key");
  336.  
  337. return url;
  338. }
  339.  
  340. private static string NormalizeConfiguredLocalAddress(string address)
  341. {
  342. var add = address.AsSpan().Trim('/');
  343. int index = add.IndexOf('/');
  344. if (index != -1)
  345. {
  346. add = add.Slice(index + 1);
  347. }
  348.  
  349. return add.TrimStart('/').ToString();
  350. }
  351.  
  352. private bool ValidateHost(string host)
  353. {
  354. var hosts = _config
  355. .Configuration
  356. .LocalNetworkAddresses
  357. .Select(NormalizeConfiguredLocalAddress)
  358. .ToList();
  359.  
  360. if (hosts.Count == 0)
  361. {
  362. return true;
  363. }
  364.  
  365. host = host ?? string.Empty;
  366.  
  367. if (_networkManager.IsInPrivateAddressSpace(host))
  368. {
  369. hosts.Add("localhost");
  370. hosts.Add("127.0.0.1");
  371.  
  372. return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1);
  373. }
  374.  
  375. return true;
  376. }
  377.  
  378. private bool ValidateRequest(string remoteIp, bool isLocal)
  379. {
  380. if (isLocal)
  381. {
  382. return true;
  383. }
  384.  
  385. if (_config.Configuration.EnableRemoteAccess)
  386. {
  387. var addressFilter = _config.Configuration.RemoteIPFilter.Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
  388.  
  389. if (addressFilter.Length > 0 && !_networkManager.IsInLocalNetwork(remoteIp))
  390. {
  391. if (_config.Configuration.IsRemoteIPFilterBlacklist)
  392. {
  393. return !_networkManager.IsAddressInSubnets(remoteIp, addressFilter);
  394. }
  395. else
  396. {
  397. return _networkManager.IsAddressInSubnets(remoteIp, addressFilter);
  398. }
  399. }
  400. }
  401. else
  402. {
  403. if (!_networkManager.IsInLocalNetwork(remoteIp))
  404. {
  405. return false;
  406. }
  407. }
  408.  
  409. return true;
  410. }
  411.  
  412. private bool ValidateSsl(string remoteIp, string urlString)
  413. {
  414. if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy)
  415. {
  416. if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1)
  417. {
  418. // These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected
  419. if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1
  420. || urlString.IndexOf("dlna/", StringComparison.OrdinalIgnoreCase) != -1)
  421. {
  422. return true;
  423. }
  424.  
  425. if (!_networkManager.IsInLocalNetwork(remoteIp))
  426. {
  427. return false;
  428. }
  429. }
  430. }
  431.  
  432. return true;
  433. }
  434.  
  435. /// <summary>
  436. /// Overridable method that can be used to implement a custom hnandler
  437. /// </summary>
  438. public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
  439. {
  440. var stopWatch = new Stopwatch();
  441. stopWatch.Start();
  442. var httpRes = httpReq.Response;
  443. string urlToLog = null;
  444. string remoteIp = httpReq.RemoteIp;
  445.  
  446. try
  447. {
  448. if (_disposed)
  449. {
  450. httpRes.StatusCode = 503;
  451. httpRes.ContentType = "text/plain";
  452. await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false);
  453. return;
  454. }
  455.  
  456. if (!ValidateHost(host))
  457. {
  458. httpRes.StatusCode = 400;
  459. httpRes.ContentType = "text/plain";
  460. await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false);
  461. return;
  462. }
  463.  
  464. if (!ValidateRequest(remoteIp, httpReq.IsLocal))
  465. {
  466. httpRes.StatusCode = 403;
  467. httpRes.ContentType = "text/plain";
  468. await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false);
  469. return;
  470. }
  471.  
  472. if (!ValidateSsl(httpReq.RemoteIp, urlString))
  473. {
  474. RedirectToSecureUrl(httpReq, httpRes, urlString);
  475. return;
  476. }
  477.  
  478. if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
  479. {
  480. httpRes.StatusCode = 200;
  481. httpRes.Headers.Add("Access-Control-Allow-Origin", "*");
  482. httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
  483. httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
  484. httpRes.ContentType = "text/plain";
  485. await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
  486. return;
  487. }
  488.  
  489. urlToLog = GetUrlToLog(urlString);
  490.  
  491. if (string.Equals(localPath, _baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
  492. || string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
  493. || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
  494. || string.IsNullOrEmpty(localPath)
  495. || !localPath.StartsWith(_baseUrlPrefix))
  496. {
  497. // Always redirect back to the default path if the base prefix is invalid or missing
  498. _logger.LogDebug("Normalizing a URL at {0}", localPath);
  499. httpRes.Redirect(_baseUrlPrefix + "/" + _defaultRedirectPath);
  500. return;
  501. }
  502.  
  503. if (!string.IsNullOrEmpty(GlobalResponse))
  504. {
  505. // We don't want the address pings in ApplicationHost to fail
  506. if (localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
  507. {
  508. httpRes.StatusCode = 503;
  509. httpRes.ContentType = "text/html";
  510. await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false);
  511. return;
  512. }
  513. }
  514.  
  515. var handler = GetServiceHandler(httpReq);
  516. if (handler != null)
  517. {
  518. await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false);
  519. }
  520. else
  521. {
  522. await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false);
  523. }
  524. }
  525. catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException)
  526. {
  527. await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
  528. }
  529. catch (SecurityException ex)
  530. {
  531. await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
  532. }
  533. catch (Exception ex)
  534. {
  535. var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
  536.  
  537. await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false);
  538. }
  539. finally
  540. {
  541. if (httpRes.StatusCode >= 500)
  542. {
  543. _logger.LogDebug("Sending HTTP Response 500 in response to {Url}", urlToLog);
  544. }
  545.  
  546. stopWatch.Stop();
  547. var elapsed = stopWatch.Elapsed;
  548. if (elapsed.TotalMilliseconds > 500)
  549. {
  550. _logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
  551. }
  552. }
  553. }
  554.  
  555. // Entry point for HttpListener
  556. public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
  557. {
  558. var pathInfo = httpReq.PathInfo;
  559.  
  560. pathInfo = ServiceHandler.GetSanitizedPathInfo(pathInfo, out string contentType);
  561. var restPath = ServiceController.GetRestPathForRequest(httpReq.HttpMethod, pathInfo);
  562. if (restPath != null)
  563. {
  564. return new ServiceHandler(restPath, contentType);
  565. }
  566.  
  567. _logger.LogError("Could not find handler for {PathInfo}", pathInfo);
  568. return null;
  569. }
  570.  
  571. private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url)
  572. {
  573. if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
  574. {
  575. var builder = new UriBuilder(uri)
  576. {
  577. Port = _config.Configuration.PublicHttpsPort,
  578. Scheme = "https"
  579. };
  580. url = builder.Uri.ToString();
  581. }
  582.  
  583. httpRes.Redirect(url);
  584. }
  585.  
  586. /// <summary>
  587. /// Adds the rest handlers.
  588. /// </summary>
  589. /// <param name="services">The services.</param>
  590. /// <param name="listeners"></param>
  591. /// <param name="urlPrefixes"></param>
  592. public void Init(IEnumerable<IService> services, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes)
  593. {
  594. _webSocketListeners = listeners.ToArray();
  595. UrlPrefixes = urlPrefixes.ToArray();
  596. ServiceController = new ServiceController();
  597.  
  598. var types = services.Select(r => r.GetType());
  599. ServiceController.Init(this, types);
  600.  
  601. ResponseFilters = new Action<IRequest, HttpResponse, object>[]
  602. {
  603. new ResponseFilter(_logger).FilterResponse
  604. };
  605. }
  606.  
  607. public RouteAttribute[] GetRouteAttributes(Type requestType)
  608. {
  609. var routes = requestType.GetTypeInfo().GetCustomAttributes<RouteAttribute>(true).ToList();
  610. var clone = routes.ToList();
  611.  
  612. foreach (var route in clone)
  613. {
  614. routes.Add(new RouteAttribute(NormalizeCustomRoutePath(route.Path), route.Verbs)
  615. {
  616. Notes = route.Notes,
  617. Priority = route.Priority,
  618. Summary = route.Summary
  619. });
  620.  
  621. routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
  622. {
  623. Notes = route.Notes,
  624. Priority = route.Priority,
  625. Summary = route.Summary
  626. });
  627.  
  628. routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs)
  629. {
  630. Notes = route.Notes,
  631. Priority = route.Priority,
  632. Summary = route.Summary
  633. });
  634. }
  635.  
  636. return routes.ToArray();
  637. }
  638.  
  639. public Func<string, object> GetParseFn(Type propertyType)
  640. {
  641. return _funcParseFn(propertyType);
  642. }
  643.  
  644. public void SerializeToJson(object o, Stream stream)
  645. {
  646. _jsonSerializer.SerializeToStream(o, stream);
  647. }
  648.  
  649. public void SerializeToXml(object o, Stream stream)
  650. {
  651. _xmlSerializer.SerializeToStream(o, stream);
  652. }
  653.  
  654. public Task<object> DeserializeXml(Type type, Stream stream)
  655. {
  656. return Task.FromResult(_xmlSerializer.DeserializeFromStream(type, stream));
  657. }
  658.  
  659. public Task<object> DeserializeJson(Type type, Stream stream)
  660. {
  661. return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
  662. }
  663.  
  664. public Task ProcessWebSocketRequest(HttpContext context)
  665. {
  666. return _socketListener.ProcessWebSocketRequest(context);
  667. }
  668.  
  669. private string NormalizeEmbyRoutePath(string path)
  670. {
  671. _logger.LogDebug("Normalizing /emby route");
  672. return _baseUrlPrefix + "/emby" + NormalizeUrlPath(path);
  673. }
  674.  
  675. private string NormalizeMediaBrowserRoutePath(string path)
  676. {
  677. _logger.LogDebug("Normalizing /mediabrowser route");
  678. return _baseUrlPrefix + "/mediabrowser" + NormalizeUrlPath(path);
  679. }
  680.  
  681. private string NormalizeCustomRoutePath(string path)
  682. {
  683. _logger.LogDebug("Normalizing custom route {0}", path);
  684. return _baseUrlPrefix + NormalizeUrlPath(path);
  685. }
  686.  
  687. /// <inheritdoc />
  688. public void Dispose()
  689. {
  690. Dispose(true);
  691. GC.SuppressFinalize(this);
  692. }
  693.  
  694. protected virtual void Dispose(bool disposing)
  695. {
  696. if (_disposed) return;
  697.  
  698. if (disposing)
  699. {
  700. Stop();
  701. }
  702.  
  703. _disposed = true;
  704. }
  705.  
  706. /// <summary>
  707. /// Processes the web socket message received.
  708. /// </summary>
  709. /// <param name="result">The result.</param>
  710. private Task ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
  711. {
  712. if (_disposed)
  713. {
  714. return Task.CompletedTask;
  715. }
  716.  
  717. _logger.LogDebug("Websocket message received: {0}", result.MessageType);
  718.  
  719. IEnumerable<Task> GetTasks()
  720. {
  721. foreach (var x in _webSocketListeners)
  722. {
  723. yield return x.ProcessMessageAsync(result);
  724. }
  725. }
  726.  
  727. return Task.WhenAll(GetTasks());
  728. }
  729. }
  730. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement