HTTP keep alive with console application and iHttpHandler is losing connection

26 Views Asked by At

I have this simple HTTPHandler which simply sets keep-alive to true and saves the response object to a list so we can write to it later. Then the global module further down has a timer which writes some content to the response object every 5 seconds, and that's where it's failing because the response object is no longer useable :

Imports System.Web
Imports System.Web.Services

Public Class a
    Implements IHttpHandler

    Public Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest

        context.Response.ContentType = "text/event-stream"
        context.Response.CacheControl = "no-cache"
        context.Response.Headers.Add("Connection", "keep-alive")
        context.Response.Headers.Add("keep-alive", "")

        ' Register the client (response) with the global SSEClients list
        RegisterClient(context.Response)

        context.Response.Write("Session Started")

        ' Don't close the response stream after this method; let the client handle it
        context.Response.BufferOutput = False

    End Sub

    Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

Plus a global (static) module which is intended to feed strings every 5 seconds to the console application without requiring a new HTTPGET:

Friend Module Globals

    Public SSEClients As New List(Of HttpResponse)
    Public Index As Integer = 1

    Private timer As New Threading.Timer(Sub() SendSSEEvent(), Nothing, 0, 5000)

    Public Sub RegisterClient(response As HttpResponse)

        SSEClients.Add(response)

    End Sub

    Private Sub SendSSEEvent()
        Dim eventData As String = "data: Hello, World! " & Index & vbCrLf & vbCrLf
        Index += 1
        Dim ssecopy As New List(Of HttpResponse)(SSEClients)
        For Each res As HttpResponse In ssecopy
            Try
                res.Write(eventData)
                res.Flush()
            Catch
                Try
                    SSEClients.Remove(res)
                Catch
                End Try
            End Try
        Next
    End Sub
End Module

And the client attempting to use this is a simple console application

Module Module1
    Sub Main()

        Console.WriteLine("Listening for SSE events. Press Enter to exit.")

        Dim url As String = "https://localhost:44369/a.ashx"

        Dim req As System.Net.HttpWebRequest = CType(System.Net.WebRequest.Create(url), System.Net.HttpWebRequest)

        req.KeepAlive = True
        req.Timeout = Threading.Timeout.Infinite

        Using res As System.Net.HttpWebResponse = CType(req.GetResponse, System.Net.HttpWebResponse)
            Using str As System.IO.Stream = res.GetResponseStream
                Using rdr As New System.IO.StreamReader(str)

                    While True
                        Dim line As String = rdr.ReadLine()
                        If line IsNot Nothing Then
                            Console.WriteLine(line)
                        End If
                    End While

                End Using
            End Using
        End Using

    End Sub

End Module

This was working fine with an asp.net webform (i.e. an aspx page) but since I switched to a httphandler it has not worked since. The problem is that once the first "Session Started" has been written by the console application, the response object "res" in the client code is no longer useable, meaning that res.IsClientConnected always equal false.

How can I get this working so that the response object stays alive and the server can feed the client with strings every few seconds without making a new request ? Bard and chatgpt were not able to help despite a whole day spent trying different things.

0

There are 0 best solutions below