WebSockets and accessing Captures in Raw endpoints.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

WebSockets and accessing Captures in Raw endpoints.

danielwilsonthomas
Hello,

I'm trying to write an WebSocket endpoint for an API using servant and wai-websockets. Wai-websockets provides websocketsOr :: ConnectionOptions -> ServerApp -> Application -> Application which can be used to to create an Application that can then be plugged in to a Raw endpoint. So far, this works great:

newtype SessionChannel = SessionChannel (SessionId, (TChan SessionMessage))
type SessionChannelDB = IxSet SessionChannel

type WebsocketsAPI = Capture "session_id" SessionId :> "questions" :> Raw

websocketsAPI :: Proxy WebsocketsAPI
websocketsAPI = Proxy

websocketsServer ::
    AcidState App ->
    TVar SessionChannelDB ->
    Server WebsocketsAPI
websocketsServer app sessionChannelDB = \ req ->
    websocketsOr
        defaultConnectionOptions
        (websocketsApp app sessionChannelDB req)
        (backupApp app)

websocketsApp app sessionChannelDB sessionId pending = do
    conn <- acceptRequest pending
    sendTextData conn ("Hello, Client" :: Text)

I can successfully establish a websocket connection and send the message "Hello, Client" through it. You'll notice the IxSet where a TChan broadcasting SessionMessage's is keyed on a SessionId, and there's a Capture for a SessionId in the path. I'd like to be able to look up the appropriate SessionChannel by its SessionId when a client requests this endpoint. However, I don't get that value as a Capture like I would with a normal Verb. Instead I only get the raw WAI Request. I've experimented with adding a new WebSocket endpoint and using that instead of Raw, modifying the HasServer instance for Raw until I get what I want, but I get lost trying to figure out how the Capture's would flow through and still keep Server WebsocketsAPI = Application (or SessionId -> Application). I've also looked at the servant-subscriber package, but it seems to make too many assumptions about how I'd use and construct these websockets and doesn't quite fit my needs. Does anyone have any suggestions on how to proceed? I'd like to make use of the existing servant machinery for parsing request paths as much as possible.

Thanks!

--
Reply | Threaded
Open this post in threaded view
|

Re: WebSockets and accessing Captures in Raw endpoints.

Julian Arni-4
Hi,


On Mon, Aug 15, 2016 at 07:45:17PM -0700, [hidden email] wrote:

> Hello,
>
> I'm trying to write an WebSocket endpoint for an API using servant and
> wai-websockets. Wai-websockets provides websocketsOr :: ConnectionOptions
> -> ServerApp -> Application -> Application which can be used to to create
> an Application that can then be plugged in to a Raw endpoint. So far, this
> works great:
>
> newtype SessionChannel = SessionChannel (SessionId, (TChan SessionMessage))
> type SessionChannelDB = IxSet SessionChannel
>
> type WebsocketsAPI = Capture "session_id" SessionId :> "questions" :> Raw
>
> websocketsAPI :: Proxy WebsocketsAPI
> websocketsAPI = Proxy
>
> websocketsServer ::
>     AcidState App ->
>     TVar SessionChannelDB ->
>     Server WebsocketsAPI
> websocketsServer app sessionChannelDB = \ req ->
>     websocketsOr
>         defaultConnectionOptions
>         (websocketsApp app sessionChannelDB req)
>         (backupApp app)
I'm assuming the type of websocketsApp is

  websocketsApp :: AcidState App -> TVar SessionChannelDB -> SessionId ->
  PendingConnection -> IO ()

So that in turn the 'req' lambda binding in websocketsServer above is of type
'SessionId'. Indeed, I'm pretty sure 'Server WebsocketsAPI' is equal to
'SessionId -> Application'. Just to be sure (because the binding 'req' seems
strangely named) - are you not confusing the 'Request' from the 'Application'
type synonym with 'SessionId"? 'sessionId' isn't used anywhere in the body of
'websocketsApp', so you wouldn't get a type error.


>
> websocketsApp app sessionChannelDB sessionId pending = do
>     conn <- acceptRequest pending
>     sendTextData conn ("Hello, Client" :: Text)
>
> I can successfully establish a websocket connection and send the message
> "Hello, Client" through it. You'll notice the IxSet where a TChan
> broadcasting SessionMessage's is keyed on a SessionId, and there's a Capture
> for a SessionId in the path. I'd like to be able to look up the appropriate
> SessionChannel by its SessionId when a client requests this endpoint.
> However, I don't get that value as a Capture like I would with a normal Verb.
> Instead I only get the raw WAI Request. I've experimented with adding a new
> WebSocket endpoint and using that instead of Raw, modifying the HasServer
> instance for Raw until I get what I want, but I get lost trying to figure
> out how the Capture's would flow through and still keep Server
> WebsocketsAPI = Application (or SessionId -> Application). I've also looked
> at the servant-subscriber package, but it seems to make too many
> assumptions about how I'd use and construct these websockets and doesn't
> quite fit my needs. Does anyone have any suggestions on how to proceed? I'd
> like to make use of the existing servant machinery for parsing request
> paths as much as possible.
>
> Thanks!
>
> --
>
>
>
>
>

--
Julian K. Arni
Haskell Consultant, Turing Jump
https://turingjump.com

--






signature.asc (484 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: WebSockets and accessing Captures in Raw endpoints.

danielwilsonthomas
Thanks Julian. I think you're right in that I was confusing the type of req. I remember seeing GHC infer it as a raw WAI Request at some point, but I'm seeing it as a SessionId right now. It seems like everything is working smoothly now. Thanks for your help.

On Tuesday, 16 August 2016 12:46:14 UTC-4, Julian Arni wrote:
Hi,


On Mon, Aug 15, 2016 at 07:45:17PM -0700, <a href="javascript:" target="_blank" gdf-obfuscated-mailto="yFdSvb50EwAJ" rel="nofollow" onmousedown="this.href=&#39;javascript:&#39;;return true;" onclick="this.href=&#39;javascript:&#39;;return true;">danielwil...@... wrote:

> Hello,
>
> I'm trying to write an WebSocket endpoint for an API using servant and
> wai-websockets. Wai-websockets provides websocketsOr :: ConnectionOptions
> -> ServerApp -> Application -> Application which can be used to to create
> an Application that can then be plugged in to a Raw endpoint. So far, this
> works great:
>
> newtype SessionChannel = SessionChannel (SessionId, (TChan SessionMessage))
> type SessionChannelDB = IxSet SessionChannel
>
> type WebsocketsAPI = Capture "session_id" SessionId :> "questions" :> Raw
>
> websocketsAPI :: Proxy WebsocketsAPI
> websocketsAPI = Proxy
>
> websocketsServer ::
>     AcidState App ->
>     TVar SessionChannelDB ->
>     Server WebsocketsAPI
> websocketsServer app sessionChannelDB = \ req ->
>     websocketsOr
>         defaultConnectionOptions
>         (websocketsApp app sessionChannelDB req)
>         (backupApp app)

I'm assuming the type of websocketsApp is

  websocketsApp :: AcidState App -> TVar SessionChannelDB -> SessionId ->
  PendingConnection -> IO ()

So that in turn the 'req' lambda binding in websocketsServer above is of type
'SessionId'. Indeed, I'm pretty sure 'Server WebsocketsAPI' is equal to
'SessionId -> Application'. Just to be sure (because the binding 'req' seems
strangely named) - are you not confusing the 'Request' from the 'Application'
type synonym with 'SessionId"? 'sessionId' isn't used anywhere in the body of
'websocketsApp', so you wouldn't get a type error.


>
> websocketsApp app sessionChannelDB sessionId pending = do
>     conn <- acceptRequest pending
>     sendTextData conn ("Hello, Client" :: Text)
>
> I can successfully establish a websocket connection and send the message
> "Hello, Client" through it. You'll notice the IxSet where a TChan
> broadcasting SessionMessage's is keyed on a SessionId, and there's a Capture
> for a SessionId in the path. I'd like to be able to look up the appropriate
> SessionChannel by its SessionId when a client requests this endpoint.
> However, I don't get that value as a Capture like I would with a normal Verb.
> Instead I only get the raw WAI Request. I've experimented with adding a new
> WebSocket endpoint and using that instead of Raw, modifying the HasServer
> instance for Raw until I get what I want, but I get lost trying to figure
> out how the Capture's would flow through and still keep Server
> WebsocketsAPI = Application (or SessionId -> Application). I've also looked
> at the servant-subscriber package, but it seems to make too many
> assumptions about how I'd use and construct these websockets and doesn't
> quite fit my needs. Does anyone have any suggestions on how to proceed? I'd
> like to make use of the existing servant machinery for parsing request
> paths as much as possible.
>
> Thanks!
>
> --
>
>
>
>
>


--
Julian K. Arni
Haskell Consultant, Turing Jump
<a href="https://turingjump.com" target="_blank" rel="nofollow" onmousedown="this.href=&#39;https://www.google.com/url?q\x3dhttps%3A%2F%2Fturingjump.com\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH6sHpinvVIkDnpIrD3HV41Odn4ng&#39;;return true;" onclick="this.href=&#39;https://www.google.com/url?q\x3dhttps%3A%2F%2Fturingjump.com\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH6sHpinvVIkDnpIrD3HV41Odn4ng&#39;;return true;">https://turingjump.com

--