0

I want to embed novnc console in an iframe into a .net web application. My openstack is configured to recieve http requests and my web application sends https requests. When I use iframe to show my novnc console in asp.net web application, the console sends WSS request to my server but the openstack horizon accepts WS requests, because of that I create a middleware to convert requests from https to http & WSS to WS but it didn't work, I don't know what is the problem, my console is shown but the connection is closed immediately.

my reverse proxy for handle http request is:


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;
        }
    }
}

and my web socket middle ware

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;

    }
}

when i load the page this is my wss request

novnc console in i frame

js errors

wss request to middle ware .

1 Answers1

0

Use the IHttpForwarder from YARP https://microsoft.github.io/reverse-proxy/articles/direct-forwarding.html. It handles websocket, grpc etc etc.

davidfowl
  • 37,120
  • 7
  • 93
  • 103
  • thanks for reply , i immplement that in asp.net core but it didnt works for me, its works fine for https request but the web socket is request directly to my default page so its myproblem and requestis not forwarded – Shervin Souri Feb 02 '23 at 08:54
  • Can you show the updated code with YARP? – davidfowl Feb 03 '23 at 00:20