Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

157 строки
5.8 KiB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. using DotNetty.Buffers;
  6. using DotNetty.Codecs.Http;
  7. using DotNetty.Codecs.Http.WebSockets;
  8. using DotNetty.Common.Utilities;
  9. using DotNetty.Transport.Channels;
  10. using static DotNetty.Codecs.Http.HttpVersion;
  11. using static DotNetty.Codecs.Http.HttpResponseStatus;
  12. using Microsoft.Extensions.Logging;
  13. using JT1078.DotNetty.Core.Session;
  14. using System.Text.RegularExpressions;
  15. namespace JT1078.DotNetty.WebSocket.Handlers
  16. {
  17. public sealed class JT1078WebSocketServerHandler : SimpleChannelInboundHandler<object>
  18. {
  19. const string WebsocketPath = "/jt1078live";
  20. WebSocketServerHandshaker handshaker;
  21. private readonly ILogger<JT1078WebSocketServerHandler> logger;
  22. private readonly JT1078WebSocketSessionManager jT1078WebSocketSessionManager;
  23. public JT1078WebSocketServerHandler(
  24. JT1078WebSocketSessionManager jT1078WebSocketSessionManager,
  25. ILoggerFactory loggerFactory)
  26. {
  27. this.jT1078WebSocketSessionManager = jT1078WebSocketSessionManager;
  28. logger = loggerFactory.CreateLogger<JT1078WebSocketServerHandler>();
  29. }
  30. public override void ChannelInactive(IChannelHandlerContext context)
  31. {
  32. if (logger.IsEnabled(LogLevel.Information))
  33. {
  34. logger.LogInformation(context.Channel.Id.AsShortText());
  35. }
  36. jT1078WebSocketSessionManager.RemoveSessionByChannel(context.Channel);
  37. base.ChannelInactive(context);
  38. }
  39. protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
  40. {
  41. if (msg is IFullHttpRequest request)
  42. {
  43. this.HandleHttpRequest(ctx, request);
  44. }
  45. else if (msg is WebSocketFrame frame)
  46. {
  47. this.HandleWebSocketFrame(ctx, frame);
  48. }
  49. }
  50. public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();
  51. void HandleHttpRequest(IChannelHandlerContext ctx, IFullHttpRequest req)
  52. {
  53. // Handle a bad request.
  54. if (!req.Result.IsSuccess)
  55. {
  56. SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, BadRequest));
  57. return;
  58. }
  59. // Allow only GET methods.
  60. if (!Equals(req.Method, HttpMethod.Get))
  61. {
  62. SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, Forbidden));
  63. return;
  64. }
  65. if ("/favicon.ico".Equals(req.Uri))
  66. {
  67. var res = new DefaultFullHttpResponse(Http11, NotFound);
  68. SendHttpResponse(ctx, req, res);
  69. return;
  70. }
  71. // Handshake
  72. var wsFactory = new WebSocketServerHandshakerFactory(GetWebSocketLocation(req), null, true, 5 * 1024 * 1024);
  73. this.handshaker = wsFactory.NewHandshaker(req);
  74. if (this.handshaker == null)
  75. {
  76. WebSocketServerHandshakerFactory.SendUnsupportedVersionResponse(ctx.Channel);
  77. }
  78. else
  79. {
  80. this.handshaker.HandshakeAsync(ctx.Channel, req);
  81. var uriSpan = req.Uri.AsSpan();
  82. var userId = uriSpan.Slice(uriSpan.IndexOf('?')).ToString().Split('=')[1];
  83. jT1078WebSocketSessionManager.TryAdd(userId, ctx.Channel);
  84. }
  85. }
  86. void HandleWebSocketFrame(IChannelHandlerContext ctx, WebSocketFrame frame)
  87. {
  88. // Check for closing frame
  89. if (frame is CloseWebSocketFrame)
  90. {
  91. this.handshaker.CloseAsync(ctx.Channel, (CloseWebSocketFrame)frame.Retain());
  92. return;
  93. }
  94. if (frame is PingWebSocketFrame)
  95. {
  96. ctx.WriteAsync(new PongWebSocketFrame((IByteBuffer)frame.Content.Retain()));
  97. return;
  98. }
  99. if (frame is TextWebSocketFrame)
  100. {
  101. // Echo the frame
  102. ctx.WriteAsync(frame.Retain());
  103. return;
  104. }
  105. if (frame is BinaryWebSocketFrame)
  106. {
  107. // Echo the frame
  108. ctx.WriteAsync(frame.Retain());
  109. }
  110. }
  111. static void SendHttpResponse(IChannelHandlerContext ctx, IFullHttpRequest req, IFullHttpResponse res)
  112. {
  113. // Generate an error page if response getStatus code is not OK (200).
  114. if (res.Status.Code != 200)
  115. {
  116. IByteBuffer buf = Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes(res.Status.ToString()));
  117. res.Content.WriteBytes(buf);
  118. buf.Release();
  119. HttpUtil.SetContentLength(res, res.Content.ReadableBytes);
  120. }
  121. // Send the response and close the connection if necessary.
  122. Task task = ctx.Channel.WriteAndFlushAsync(res);
  123. if (!HttpUtil.IsKeepAlive(req) || res.Status.Code != 200)
  124. {
  125. task.ContinueWith((t, c) => ((IChannelHandlerContext)c).CloseAsync(),
  126. ctx, TaskContinuationOptions.ExecuteSynchronously);
  127. }
  128. }
  129. public override void ExceptionCaught(IChannelHandlerContext ctx, Exception e)
  130. {
  131. logger.LogError(e, ctx.Channel.Id.AsShortText());
  132. jT1078WebSocketSessionManager.RemoveSessionByChannel(ctx.Channel);
  133. ctx.CloseAsync();
  134. }
  135. static string GetWebSocketLocation(IFullHttpRequest req)
  136. {
  137. bool result = req.Headers.TryGet(HttpHeaderNames.Host, out ICharSequence value);
  138. string location= value.ToString() + WebsocketPath;
  139. return "ws://" + location;
  140. }
  141. }
  142. }