Module Websocket_async

Module Websocket_async: websocket library for Async

This module implements a websocket client and server library in the spirit of the otherwise similar TCP functions of the Lwt_io module. The websocket protocol add message framing in addition of simple TCP communication, and this library implement framing and unframing of messages.

module Frame = Websocket.Frame
val client : ?name:string -> ?extra_headers:Cohttp.Header.t -> ?random_string:(int -> string) -> ?initialized:unit Async.Ivar.t -> app_to_ws:Frame.t Async.Pipe.Reader.t -> ws_to_app:Frame.t Async.Pipe.Writer.t -> net_to_ws:Async.Reader.t -> ws_to_net:Async.Writer.t -> Uri.t -> unit Async.Deferred.Or_error.t
val client_ez : ?opcode:Frame.Opcode.t -> ?name:string -> ?extra_headers:Cohttp.Header.t -> ?heartbeat:Core.Time_ns.Span.t -> ?random_string:(int -> string) -> Uri.t -> Async.Reader.t -> Async.Writer.t -> string Async.Pipe.Reader.t * string Async.Pipe.Writer.t
val server : ?name:string -> ?check_request:(Cohttp.Request.t -> bool Async.Deferred.t) -> ?select_protocol:(string -> string option) -> reader:Async.Reader.t -> writer:Async.Writer.t -> app_to_ws:Frame.t Async.Pipe.Reader.t -> ws_to_app:Frame.t Async.Pipe.Writer.t -> unit -> unit Async.Deferred.Or_error.t

server ?request_cb reader writer app_to_ws ws_to_app () returns a thread that expects a websocket client connected to reader/writer and, after performing the handshake, will resp. read outgoing frames from app_to_ws and write incoming frames to ws_to_app. The thread is determined if any of reader, writer, app_to_ws, ws_to_app is closed. If case of an error, app_to_ws and ws_to_app will be closed. Upon reception of the client HTTP request, request_cb will be called with the request as its argument. If request_cb returns true, the connection will proceed, otherwise, the result is immediately determined to Error Exit.

val upgrade_connection : ?select_protocol:(string -> string option) -> ?ping_interval:Core.Time_ns.Span.t -> app_to_ws:Frame.t Async.Pipe.Reader.t -> ws_to_app:Frame.t Async.Pipe.Writer.t -> f:(unit -> unit Async.Deferred.t) -> Cohttp.Request.t -> Cohttp.Response.t * (Async.Reader.t -> Async.Writer.t -> unit Async.Deferred.t)

upgrade_connection ?select_protocol ?ping_interval app_to_ws ws_to_app f request returns a Cohttp_async.Server.response_action.

Just wrap the return value of this function with `Expert. You can combine responses both of HTTP `Response handler and Websocket `Expert handler.

Your handler will look like this:

let response =
  let app_to_ws, ws_write = Pipe.create () in
  let ws_read, ws_to_app = Pipe.create () in
  Websocket_async.upgrade_connection request ~app_to_ws ~ws_to_app ~f:begin fun () ->
    let rec loop () =
      let open Websocket in
      match%bind Pipe.read ws_read with
      | `Eof -> return ()
      | `Ok ({ Frame.opcode; content; _ } as frame) ->
        let open Frame in
        let frame', closed =
          match opcode with
          | Opcode.Ping -> Some (create ~opcode:Opcode.Pong ~content ()), false
          | Opcode.Close ->
            (* Immediately echo and pass this last message to the user *)
            if String.length content >= 2 then
              Some (create ~opcode:Opcode.Close
                      ~content:(String.sub content ~pos:0 ~len:2) ()), true
            else
              Some (close 100), true
          | Opcode.Pong -> None, false
          | Opcode.Text
          | Opcode.Binary -> Some frame, false
          | _ -> Some (close 1002), false
        in
        begin
          match frame' with
          | None       -> Deferred.unit
          | Some frame -> Pipe.write ws_write frame
        end >>= fun () ->
        if closed
        then Deferred.unit
        else loop ()
    in
    loop ()
  end
in
return (`Expert response)