WebsocketService.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.Extensions.DependencyInjection;
  3. using Microsoft.Extensions.Logging;
  4. using System.Data.Entity.Core.Common.EntitySql;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.WebSockets;
  8. using System.ServiceModel.Channels;
  9. namespace EVCB_OCPP.WSServer.Service.WsService;
  10. public class WebsocketService<T> where T : WsSession
  11. {
  12. public WebsocketService(IServiceProvider serviceProvider, ILogger logger)
  13. {
  14. this.serviceProvider = serviceProvider;
  15. this.logger = logger;
  16. }
  17. private readonly IServiceProvider serviceProvider;
  18. private readonly ILogger logger;
  19. public event EventHandler<T> NewSessionConnected;
  20. public async Task AcceptWebSocket(HttpContext context)
  21. {
  22. if (!context.WebSockets.IsWebSocketRequest)
  23. {
  24. return;
  25. }
  26. var portocol = await ValidateSupportedPortocol(context);
  27. if (string.IsNullOrEmpty(portocol))
  28. {
  29. return;
  30. }
  31. T data = GetSession(context);
  32. if (!await ValidateHandshake(context, data))
  33. {
  34. return;
  35. }
  36. using WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(portocol);
  37. LogHandshakeResponse(context);
  38. await AddWebSocket(webSocket, data);
  39. }
  40. internal virtual ValueTask<bool> ValidateHandshake(HttpContext context, T data)
  41. {
  42. return ValueTask.FromResult(true);
  43. }
  44. internal virtual ValueTask<string> ValidateSupportedPortocol(HttpContext context)
  45. {
  46. return ValueTask.FromResult(string.Empty);
  47. }
  48. private async Task AddWebSocket(WebSocket webSocket, T data)
  49. {
  50. data.ClientWebSocket = webSocket;
  51. NewSessionConnected?.Invoke(this, data);
  52. await data.EndConnSemaphore.WaitAsync();
  53. return;
  54. }
  55. private T GetSession(HttpContext context)
  56. {
  57. T data = serviceProvider.GetRequiredService<T>();
  58. data.Path = context?.Request?.Path;
  59. data.SessionID = context.TraceIdentifier;
  60. data.UriScheme = GetScheme(context);
  61. try
  62. {
  63. var proxyPassClientIp = context.Request.Headers["X-Forwarded-For"];
  64. foreach (var infoString in proxyPassClientIp)
  65. {
  66. foreach (var testIp in infoString.Split(','))
  67. {
  68. logger.LogDebug("X-Forwarded-For {ip}", testIp);
  69. if (IPEndPoint.TryParse(testIp, out var parseResult) &&
  70. (parseResult.AddressFamily is System.Net.Sockets.AddressFamily.InterNetwork or System.Net.Sockets.AddressFamily.InterNetworkV6)
  71. )
  72. {
  73. data.Endpoint = parseResult;
  74. break;
  75. }
  76. }
  77. if (data.Endpoint != null)
  78. {
  79. break;
  80. }
  81. }
  82. if (data.Endpoint is null)
  83. {
  84. var ipaddress = context.Connection.RemoteIpAddress;
  85. var port = context.Connection.RemotePort;
  86. data.Endpoint = new IPEndPoint(ipaddress, port);
  87. }
  88. }
  89. catch
  90. {
  91. data.Endpoint = null;
  92. }
  93. return data;
  94. }
  95. private string GetScheme(HttpContext context)
  96. {
  97. string toReturn = string.Empty;
  98. var rawScheme = string.Empty;
  99. if (context.Request.Headers.ContainsKey("x-original-host"))
  100. {
  101. rawScheme = new Uri(context.Request.Headers["x-original-host"]).Scheme;
  102. }
  103. if (context.Request.Headers.ContainsKey("X-Forwarded-Proto"))
  104. {
  105. rawScheme = context.Request.Headers["X-Forwarded-Proto"];
  106. }
  107. var origin = context.Request.Headers.Origin.FirstOrDefault();
  108. try
  109. {
  110. toReturn = new Uri(origin).Scheme;
  111. return toReturn;
  112. }
  113. catch
  114. {
  115. }
  116. if (string.IsNullOrEmpty(rawScheme))
  117. {
  118. rawScheme = context.Request.Scheme;
  119. }
  120. rawScheme = rawScheme.ToLower();
  121. if (rawScheme == "http" ||
  122. rawScheme == "ws")
  123. {
  124. return "ws";
  125. }
  126. if (rawScheme == "https" ||
  127. rawScheme == "wss")
  128. {
  129. return "wss";
  130. }
  131. return toReturn;
  132. }
  133. private void LogHandshakeResponse(HttpContext context)
  134. {
  135. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, "Date:", context.Response.Headers["Date"]);
  136. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, context.Request.Protocol + " " + context.Response.StatusCode, "Switching Protocols");
  137. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, "Upgrade:", context.Response.Headers.Upgrade);
  138. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, "Connection:", context.Response.Headers.Connection);
  139. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, "SecWebSocketAccept:", context.Response.Headers.SecWebSocketAccept);
  140. logger.LogInformation("{0} {1} {2}", context.TraceIdentifier, "SecWebSocketProtocol:", context.Response.Headers.SecWebSocketProtocol);
  141. }
  142. }