Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add routing module for pending connections #100

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

nilscc
Copy link
Contributor

@nilscc nilscc commented Apr 28, 2015

As discussed in our mails, here is my routing module proposal. The idea is based on happstacks routing module. It is "non intrusive" in that it does not require changes to any of the underlying types (PendingConnection or Connection). I also added a couple of test cases for it. I had to however raise the dependency on mtl from version >= 2.0 to >= 2.2 to be able to use Alternative and MonadPlus instances for the WebSocketsRoute monad.

Please take a look and let me know what you think.

Also, I am (temporarily) hosting generated haddock documentation at: https://nils.cc/hs/websockets/Network-WebSockets-Routing.html

Copy link
Contributor

@OlivierSohn OlivierSohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to add a thin layer of functions manipulating paths to hide this complexity from the higher level functions. These paths would be best represented using Text to have faster execution.

import Network.WebSockets
import System.FilePath -- (makeRelative, splitDirectories)

type WebSocketsRouteTy =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think performance-wise it would be beneficial to have a data here, with strict fields.

It will guarantee that objects are next-to one another in memory: it means accessing them will be faster because they have a higher probability to already be in cache.

, path
) where

import Control.Applicative
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make all imports explicit ? It's a personal preference of mine.

-- Route by subprotocol

-- | Route by available subprotocols. If any of the protocols in the header
-- matches the route will succeed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a coma between matches and the.

subprotocol proto w = do
p <- askPending'
let reqProto = getRequestSubprotocols . pendingRequest $ p
if proto `elem` reqProto then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more readable when then is on the next line, on its own

setProto :: Maybe ByteString -> WebSocketsRoute a -> WebSocketsRoute a
setProto bs r = WebSocketsRoute $ withReaderT (\(p,_,q) -> (p,bs,q)) (unWebSocketsRoute r)

setPath :: [String] -> PendingConnection -> PendingConnection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance-wise, maybe it's preferable to use Text in place of String (here and at other places in this file too)

path r = do
pending <- askPending'
case paths pending of
(p:ps) | p /= "." -> -- makeRelative turns "/" into ["."]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this whole file, I think it would be beneficial to wrap the path (de-)composition logic, to better separate concerns, and make the code more readable. For example here, instead of p /= "." with a comment as to why we don't compare with "/", I would prefer seeing not empty p or something like that.

assert =<< runClient "127.0.0.1" port "/trailing/?q=a" (expect "trailing")
assert =<< runClient "127.0.0.1" port "/trailing?q=a" (expect "no trailing")
assert =<< runClient "127.0.0.1" port "/full?path=reply" (expect "/full?path=reply")
assert =<< runClient "127.0.0.1" port "/full/2?path=reply" (expect "/2?path=reply")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is possible to have / at the right of ?, maybe we should add a test with a path like "/foo/bar?q=ab/cd/e"

@jaspervdj
Copy link
Owner

I'm currently not planning to merge this, as I want to limit the scope of the websockets package to just WebSockets. If you're using the websockets library together with a framework like snap or warp, you can already use the existing routing infrastructure that's in there. If you really want to run your own, it's maybe better to pick a small package like web-routes.

@OlivierSohn
Copy link
Contributor

@jaspervdj Thanks for the info, I don't need routing right now, but will look into the packages you mention later if needed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants