在ASP.NET Core 6中嵌入noVNC的WebSocket代理实现故障排查
问题描述
我需要在.NET Web应用的iframe中嵌入noVNC控制台,我的OpenStack配置仅接收HTTP请求,但Web应用使用HTTPS协议。当在ASP.NET Web应用里用iframe展示noVNC控制台时,控制台会向我的服务器发送WSS请求,可OpenStack Horizon只支持WS请求。我编写了中间件实现HTTPS转HTTP、WSS转WS,但控制台能显示却立即断开连接,附上相关代码及问题截图:
HTTP反向代理中间件
namespace ReverseProxyApplication { public class ReverseProxyMiddleware { private static readonly HttpClient _httpClient = new HttpClient(); private readonly RequestDelegate _nextMiddleware; public ReverseProxyMiddleware(RequestDelegate nextMiddleware) { _nextMiddleware = nextMiddleware; } public async Task Invoke(HttpContext context) { var targetUri = BuildTargetUri(context.Request); if (targetUri != null) { var targetRequestMessage = CreateTargetMessage(context, targetUri); using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted)) { context.Response.StatusCode = (int)responseMessage.StatusCode; CopyFromTargetResponseHeaders(context, responseMessage); await responseMessage.Content.CopyToAsync(context.Response.Body); } return; } await _nextMiddleware(context); } private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri) { var requestMessage = new HttpRequestMessage(); CopyFromOriginalRequestContentAndHeaders(context, requestMessage); requestMessage.RequestUri = targetUri; requestMessage.Headers.Host = targetUri.Host; requestMessage.Method = GetMethod(context.Request.Method); return requestMessage; } private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage) { var requestMethod = context.Request.Method; if (!HttpMethods.IsGet(requestMethod) && !HttpMethods.IsHead(requestMethod) && !HttpMethods.IsDelete(requestMethod) && !HttpMethods.IsTrace(requestMethod)) { var streamContent = new StreamContent(context.Request.Body); requestMessage.Content = streamContent; } foreach (var header in context.Request.Headers) { requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); } } private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage) { foreach (var header in responseMessage.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } foreach (var header in responseMessage.Content.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } context.Response.Headers.Remove("transfer-encoding"); } private static HttpMethod GetMethod(string method) { if (HttpMethods.IsDelete(method)) return HttpMethod.Delete; if (HttpMethods.IsGet(method)) return HttpMethod.Get; if (HttpMethods.IsHead(method)) return HttpMethod.Head; if (HttpMethods.IsOptions(method)) return HttpMethod.Options; if (HttpMethods.IsPost(method)) return HttpMethod.Post; if (HttpMethods.IsPut(method)) return HttpMethod.Put; if (HttpMethods.IsTrace(method)) return HttpMethod.Trace; return new HttpMethod(method); } private Uri BuildTargetUri(HttpRequest request) { Uri targetUri = null; if (request.Path.StartsWithSegments("/horizon", out var remainingPath)) { targetUri = new Uri("http://192.168.66.11:6080" + remainingPath); } return targetUri; } } }
WebSocket中间件
public class WebSocketMiddleware { private static ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>(); private readonly RequestDelegate _next; public WebSocketMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) { await _next.Invoke(context); return; } var buffer = new ArraySegment<byte>(new byte[8192]); Uri targetUri = new Uri("ws://192.168.66.11:6080/" + context.Request.QueryString.Value); ClientWebSocket _websocketClient = new ClientWebSocket(); await _websocketClient.ConnectAsync(targetUri, CancellationToken.None); // if (_websocketClient.State == WebSocketState.Open) // { // // await _websocketClient.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None); // var result = await _websocketClient.ReceiveAsync(buffer, CancellationToken.None); // if (result.MessageType == WebSocketMessageType.Close) // { // await _websocketClient.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); // } // } CancellationToken ct = context.RequestAborted; WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync(); var socketId = Guid.NewGuid().ToString(); _sockets.TryAdd(socketId, currentSocket); foreach (var socket in _sockets) { if (socket.Value.State != WebSocketState.Open) { continue; } await SendStringAsync(socket.Value, _websocketClient, buffer, ct); } while (true) { if (ct.IsCancellationRequested) { break; } var response = await ReceiveStringAsync(currentSocket, _websocketClient, context, ct); if (response != null) { if (currentSocket.State != WebSocketState.Open) { break; } //continue; foreach (var socket in _sockets) { if (socket.Value.State != WebSocketState.Open) { continue; } await SendStringAsync(socket.Value, _websocketClient, response, ct); } } } WebSocket dummy; _sockets.TryRemove(socketId, out dummy); await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct); currentSocket.Dispose(); } private static Task SendStringAsync(WebSocket socket, ClientWebSocket _websocketClient, ArraySegment<byte> data, CancellationToken ct = default(CancellationToken)) { //var buffer = Encoding.UTF8.GetBytes(data); var segment = new ArraySegment<byte>(new byte[8192]); if (_websocketClient.State == WebSocketState.Open) { _websocketClient.ReceiveAsync(segment, CancellationToken.None); //_websocketClient.SendAsync(segment, WebSocketMessageType.Binary, true, CancellationToken.None); } return socket.SendAsync(segment, WebSocketMessageType.Binary, true, ct); } private static async Task<ArraySegment<byte>> ReceiveStringAsync(WebSocket socket, ClientWebSocket _websocketClient, HttpContext context, CancellationToken ct = default(CancellationToken)) { var buffer = new ArraySegment<byte>(new byte[8192]); WebSocketReceiveResult result; do { ct.ThrowIfCancellationRequested(); result = await socket.ReceiveAsync(buffer, ct); } while (!result.EndOfMessage); // Uri targetUri = new Uri("ws://192.168.66.11:6080/" + context.Request.QueryString.Value); //ClientWebSocket _websocketClient = new ClientWebSocket(); //await _websocketClient.ConnectAsync(targetUri, CancellationToken.None); if (_websocketClient.State == WebSocketState.Open) { bool hasAllZeroes = buffer.All(singleByte => singleByte == 0); if (!hasAllZeroes) await _websocketClient.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None); //var myresult = await _websocketClient.ReceiveAsync(buffer, CancellationToken.None); } return buffer == new ArraySegment<byte>(new byte[8192]) ? null : buffer; } }
页面加载情况
内容的提问来源于stack exchange,提问作者Shervin Souri
相关产品推荐
相关产品推荐




