Search code examples
swiftmacosserver-sent-eventskitura

Server Sent Events with Kitura


I try to do a client-server application with on the client side a Angular2/typescript web site and on the server side a Kitura server in Swift on Mac OSX. On the client side, the typescript code instanciates an EventSource object :

    this.eventSource = new EventSource(this.webSocketServerUrl);
    this.eventSource.onopen = (event: Event): any => {
        console.log("ServerNotificationsService.onopen - " + JSON.stringify(event) + " " + this.eventSource.readyState);
        event.stopPropagation();
        return null;
    }
    this.eventSource.onerror = (event: sse.IOnMessageEvent) => {
        console.log("ServerNotificationsService.onerror - " + JSON.stringify(event) + " " + this.eventSource.readyState);
    }
    this.eventSource.onmessage = (event: Event): any => {
        console.log("ServerNotificationsService.onmessage - " + JSON.stringify(event) + " " + this.eventSource.readyState);
        event.stopPropagation();
        return null;
    }
    console.log("ServerNotificationsService.constructor - " + this.eventSource.readyState);

On the server side, my code to handle the GET request looks like this :

router.get("/notifications") { request, response, next in 
response.headers.append("Access-Control-Allow-Origin", value: "*")

if((request.accepts(header: "Accept", type: "text/event-stream")) != nil)
{
  response.headers.append("content-type", value: "text/event-stream; charset=utf-8")
  response.headers.append("cache-control", value: "no-cache")
  response.headers.append("connection", value: "keep-alive")
  try response.end()
  Logger.sharedInstance.verbose(msg: "Request GET /notifications OK")
}
else
{
  try response.status(.internalServerError).end()
  Logger.sharedInstance.verbose(msg: "Request GET /notifications internalServerError")
}

next()
}

and to handle the post request :

router.post("/notifications") { request, response, next in
Logger.sharedInstance.verbose(msg: "Request POST /notifications ...")
response.headers.append("Access-Control-Allow-Origin", value: "*")
response.headers.append("content-type", value: "text/event-stream; charset=utf-8")
response.headers.append("cache-control", value: "no-cache")
response.headers.append("connection", value: "keep-alive")

while (true)
{
  // wait here 5s. for the <nextMessage>
  response.send(<nextMessage>)
  try response.end()
  Logger.sharedInstance.verbose(msg: "Request POST /notifications OK")
  next()
}
}

The problem is that on the client side I receive the onopen notification, the event source's readyState pass to "Open" (1) but I receive immediately after a onerror notification and the readyState pass to "Connecting" (0). And so on : connecting, close, connecting, close, ... And in consequence the post request is never executed.

I will appreciate some help to have a code that maintains an open connexion.

Thank you,

Notux


Solution

  • Kitura does not currently support persistent, open HTTP connections. However, you might be able to replicate the functionality using WebSocket instead of server-sent events over HTTP (you will need to rewrite your frontend code to use WebSockets instead of EventSource): https://github.com/IBM-Swift/Kitura-WebSocket

    And example Kitura-WebSocket app can be found here: https://github.com/IBM-Swift/Kitura-Chat-Server