Happstack events and web page refreshing

Hello again,
I have another question for happstack users/experts:
I have a program with happstack-state and a web server with
happstack-server.
What I'd like is, whenever the MACID state is changed, that the web page is
refreshed for every clients
connected on the web server.
So I think the question is 2 fold:
- How to add an event handler on happstack-state for that?
- How to ask to the web server to refresh every clients?
I did not found infos about that in the API.
Thanks a lot,
Corentin
On Thu, Jan 13, 2011 at 9:40 PM, Corentin Dupont
Hello,
I'm using the combination happstack + digestive-functors + web-routes + blazeHTML. I'm not finding any examples on the net...
I've tried to adapt your example (thanks):
type NomicForm a = HappstackForm IO String BlazeFormHtml a
demoForm :: NomicForm (Text, Text) demoForm = (,) <$> ((TDB.label "greeting: " ++> inputNonEmpty Nothing) <* br) <*> ((TDB.label "noun: " ++> inputNonEmpty Nothing) <* br) <* (submit "submit") where br :: NomicForm () br = view H.br -- make sure the fields are not blank, show errors in line if they are inputNonEmpty :: Maybe Text -> NomicForm Text inputNonEmpty v = (inputText v `validate` (TD.check "You can not leave this field blank." (not . T.null)) <++ errors)
But I've got a problem on submit and inputText. I don't see how they are compatible with HappstackForm. NomicForm a reduces to: Form (ServerPartT IO) Input String BlazeFormHtml a
whereas the type of submit is:
submit :: Monad m
=> String -- ^ Text on the submit button
-> Form m String e BlazeFormHtml () -- ^ Submit button
Maybe I miss some instance?
BTW, I also tried to execute your exemple, but I can't install some packages.
cabal install digestive-functors-hsp
cabal: Unknown build tool trhsx
Whereas trhsx is in my PATH (under linux).
You said I need the latest happstack from darcs, why?
Cheers, Corentin
On Sun, Jan 9, 2011 at 8:36 PM, Jeremy Shaw
wrote: Hello,
newRule also needs to have the type, RoutedNomicServer. The transformation of RoutedNomicServer into NomicServer is done in the handleSite function. Something like this:
nomicSpec :: ServerHandle -> Site Route (ServerPartT IO Response) nomicSpec sh = Site { handleSite = \f url -> unRouteT (nomicSite sh url) f ...
main = do ... simpleHTTP nullConf $ siteImpl (nomicSpec sh)
Or something like that -- it's hard to tell exactly what is going on in your app based on the snippets you provided.
Also, I highly recommend using digestive functors instead of formlets. It is the successor to formlets. Same core idea, better implementation and actively maintained.
I have attached a quick demo of using:
happstack+digestive-functors+web-routes+HSP
To use it you will need the latest happstack from darcs plus:
hsp web-routes web-routes-hsp web-routes-happstack web-routes-mtl digestive-functors digestive-functors-hsp
I plan to clean up this example and document it better in the crash course for the upcoming release. Clearly things like the FormInput instance and the formPart function belong a library.
let me know if you have more questions. - jeremy
Hello,
I have difficulties mixing web-routes and forms: I have put routes in all my site, except for forms which remains with
type ServerPartT IO Response. How to make them work together?
I have: type NomicServer = ServerPartT IO type RoutedNomicServer = RouteT PlayerCommand NomicServer
newRule :: ServerHandle -> NomicServer Response newRule sh = do methodM POST -- only accept a post method mbEntry <- getData -- get the data case mbEntry of Nothing -> error $ "error: newRule" Just (NewRule name text code pn) -> do html <- nomicPageComm pn sh (submitRule name text code pn)) ok $ toResponse html
nomicPageComm :: PlayerNumber -> ServerHandle -> Comm () -> RoutedNomicServer Html nomicPageComm pn sh comm = (..)
launchWebServer :: ServerHandle -> IO () launchWebServer sh = do putStrLn "Starting web server...\nTo connect, drive your browser to \"http://localhost:8000/Login\ http://localhost:8000/Login%5C"" d <- liftIO getDataDir simpleHTTP nullConf $ mconcat [dir "postLogin" $ postLogin, fileServe [] d, dir "Login" $ ok $ toResponse $ loginPage, dir "NewRule" $ newRule sh, dir "NewGame" $ newGameWeb sh, dir "Nomic" $ do html <- implSite "http://localhost:8000/Nomic/" "" (nomicSite sh) ok $ toResponse html ]
The red line doesn't compile. I don't know how to transform a RoutedNomicServer into a NomicServer.
For the future I intend to use formlets: is these some examples of
On Sat, Jan 8, 2011 at 6:44 PM, Corentin Dupont
wrote: the programs using happstack + web-routes + formlets?
Thanks, Corentin
On Fri, Jan 7, 2011 at 5:10 PM, Jeremy Shaw
wrote: Hello,
The [(String, String)] argument is for adding query parameters.
encodePathInfo ["foo", "bar", "baz"] [("key","value")]
"foo/bar/baz?key=value"
Instead of showURL you would use showURLParams.
hope this helps!d - jeremy
On Fri, Jan 7, 2011 at 8:12 AM, Corentin Dupont
wrote: Hello Jeremy, I'm using Web routes with happstack. I'm following this tutorial: http://tutorialpedia.org/tutorials/Happstack+type+safe+URLs.html
But It seems out of synch with the latest version of web-routes:
0.23.2.
The haddock documentation seems out of date also:
encodePathInfo :: [String] -> [(String, String)] -> String
For example:
encodePathInfo [\"foo\", \"bar\", \"baz\"]
"foo/bar/baz"
And I can't figure out what this [(String, String)] is for ;)
Thanks,
Corentin

Hello, The problem is that clients are not 'connected' to the web server. The way it works (more or less) is: 1. client connects to server and sends a Request 2. server sends a Response 3. connection is terminated. So, once the page has been loaded there is no connection between the web-server and the client. Hence there is no way for the server to send anything to the clients. In HTML 5, there is something known as web sockets with does allow you to have a persistent connection to the clients. However, it does not yet have broad browser support. In fact, some browsers have temporarily removed support due to a security flaw in the design specification. There are a variety of hacks collectively known as 'comet' for trying to create a persistent connection on top of the existing HTTP 1.1 standard: http://en.wikipedia.org/wiki/Comet_(programming) That deals with trying to have the server push updates to the client. A different approach is to have the clients pull new data from the server by polling the server for changes every now and then. But that depends on how much latency you can afford for the updates. For example, if updating only once a minute is fine versus must update every second. Once you decide how to get the data to the client, then you can figure out how to track changes to the MACID database. - jeremy On Jan 17, 2011, at 4:58 AM, Corentin Dupont wrote:
Hello again, I have another question for happstack users/experts:
I have a program with happstack-state and a web server with happstack-server. What I'd like is, whenever the MACID state is changed, that the web page is refreshed for every clients connected on the web server.
So I think the question is 2 fold: - How to add an event handler on happstack-state for that? - How to ask to the web server to refresh every clients?
I did not found infos about that in the API.
Thanks a lot, Corentin

Thanks a lot for your response Jeremy.
I can see a lot of site that does update infos without the user to have to
click "refresh" (I think Facebook does?).
Do they do polling?
I think this approach would be fine for me, my app if not very fast paced.
Then I don't need to add a event handler to happstack-state ;)
This is done with something like: <META HTTP-EQUIV="*Refresh*" CONTENT="*n*
"> ?
Thanks,
Corentin
On Mon, Jan 17, 2011 at 5:41 PM, Jeremy Shaw
Hello,
The problem is that clients are not 'connected' to the web server. The way it works (more or less) is:
1. client connects to server and sends a Request 2. server sends a Response 3. connection is terminated.
So, once the page has been loaded there is no connection between the web-server and the client. Hence there is no way for the server to send anything to the clients.
In HTML 5, there is something known as web sockets with does allow you to have a persistent connection to the clients. However, it does not yet have broad browser support. In fact, some browsers have temporarily removed support due to a security flaw in the design specification.
There are a variety of hacks collectively known as 'comet' for trying to create a persistent connection on top of the existing HTTP 1.1 standard:
http://en.wikipedia.org/wiki/Comet_(programming)http://en.wikipedia.org/wiki/Comet_%28programming%29
That deals with trying to have the server push updates to the client.
A different approach is to have the clients pull new data from the server by polling the server for changes every now and then. But that depends on how much latency you can afford for the updates. For example, if updating only once a minute is fine versus must update every second.
Once you decide how to get the data to the client, then you can figure out how to track changes to the MACID database.
- jeremy
On Jan 17, 2011, at 4:58 AM, Corentin Dupont wrote:
Hello again, I have another question for happstack users/experts:
I have a program with happstack-state and a web server with happstack-server. What I'd like is, whenever the MACID state is changed, that the web page is refreshed for every clients connected on the web server.
So I think the question is 2 fold: - How to add an event handler on happstack-state for that? - How to ask to the web server to refresh every clients?
I did not found infos about that in the API.
Thanks a lot, Corentin

On Mon, 2011-01-17 at 18:25 +0100, Corentin Dupont wrote:
Thanks a lot for your response Jeremy. I can see a lot of site that does update infos without the user to have to click "refresh" (I think Facebook does?). Do they do polling?
While I'm not familiar with Facebook, I'd guess that today, most such sites are all doing polling. Especially the high-volume ones, for which leaving a connection open for every current user would be impractical. Polling doesn't have to be done with page refreshes. It can also be done with JavaScript using, e.g., the XMLHTTPRequest object. Then it would be pretty transparent to you. -- Chris

Indeed, I tried with <META HTTP-EQUIV="*Refresh*" CONTENT="*n*"> ?
and it's unusable.
It make blink the page, ungrey the "stop" button for a second and make the
fields loose the focus
so it's impossible to type in.
I'll try with XMLHTTPRequest.
On Mon, Jan 17, 2011 at 6:30 PM, Chris Smith
On Mon, 2011-01-17 at 18:25 +0100, Corentin Dupont wrote:
Thanks a lot for your response Jeremy. I can see a lot of site that does update infos without the user to have to click "refresh" (I think Facebook does?). Do they do polling?
While I'm not familiar with Facebook, I'd guess that today, most such sites are all doing polling. Especially the high-volume ones, for which leaving a connection open for every current user would be impractical.
Polling doesn't have to be done with page refreshes. It can also be done with JavaScript using, e.g., the XMLHTTPRequest object. Then it would be pretty transparent to you.
-- Chris

On Jan 17, 2011, at 2:19 PM, Corentin Dupont wrote:
Indeed, I tried with <META HTTP-EQUIV="Refresh" CONTENT="n"> ? and it's unusable. It make blink the page, ungrey the "stop" button for a second and make the fields loose the focus so it's impossible to type in.
I'll try with XMLHTTPRequest.
Right. Using the jQuery library should make it easier to do ajax requests and modify the DOM on the fly, http://jquery.com/ - jeremy

On 17 January 2011 21:50, Jeremy Shaw
On Jan 17, 2011, at 2:19 PM, Corentin Dupont wrote:
Indeed, I tried with <META HTTP-EQUIV="Refresh" CONTENT="n"> ? and it's unusable. It make blink the page, ungrey the "stop" button for a second and make the fields loose the focus so it's impossible to type in.
I'll try with XMLHTTPRequest.
Right. Using the jQuery library should make it easier to do ajax requests and modify the DOM on the fly, http://jquery.com/ - jeremy _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
A nice variation of polling is "long polling": http://en.wikipedia.org/wiki/Push_technology#Long_polling This can easily be accomplished in Haskell by having an MVar per session. Initially an empty MVar is created per new session. When a client makes a request, the server thread that handles the request takes the MVar belonging to the session. This thread will block until the MVar is filled. When the server has an update it will fill all the MVars. This causes all the blocked threads to continue with sending a response to the client notifying it about the update. Like Jeremy said it's a good idea to make these update requests asynchronous. Regards, Bas

Thanks.
There seems to be several technologies to realize this push or polling.
Is what you explained feasible on the user's side of happstack-server (I
mean, if I can do it myself)?
If I take an empty MVar in my ServerPartTs, which are read over client's
request,
I think that nothing will be sent back to the browser and it will remain
blank!
Is that to be combined with an HTTP refresh timer on the client side?
By sessions, you mean sessions that I create myself for every client
connected?
Regards,
Corentin
On Wed, Jan 19, 2011 at 2:21 PM, Bas van Dijk
On 17 January 2011 21:50, Jeremy Shaw
wrote: On Jan 17, 2011, at 2:19 PM, Corentin Dupont wrote:
Indeed, I tried with <META HTTP-EQUIV="Refresh" CONTENT="n"> ? and it's unusable. It make blink the page, ungrey the "stop" button for a second and make
the
fields loose the focus so it's impossible to type in.
I'll try with XMLHTTPRequest.
Right. Using the jQuery library should make it easier to do ajax requests and modify the DOM on the fly, http://jquery.com/ - jeremy _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
A nice variation of polling is "long polling":
http://en.wikipedia.org/wiki/Push_technology#Long_polling
This can easily be accomplished in Haskell by having an MVar per session. Initially an empty MVar is created per new session. When a client makes a request, the server thread that handles the request takes the MVar belonging to the session. This thread will block until the MVar is filled. When the server has an update it will fill all the MVars. This causes all the blocked threads to continue with sending a response to the client notifying it about the update.
Like Jeremy said it's a good idea to make these update requests asynchronous.
Regards,
Bas

Hi,
I've not really followed the thread, but on the client side, for
long-polling, you issue an XMLHttpRequest that will receive an answer
from the server only when the server has something to push to the
client (maybe done with a thread waiting on an MVar). XHR is
asynchronous as you have to supply a callback to process the server's
response.
Usually an XHR request is done from an already loaded page and will
update part of that page with the content of the server's response. So
no blank page.
(So no timer needed either, this is done asynchronously with callbacks.)
HTH,
Thu
2011/1/19 Corentin Dupont
Thanks. There seems to be several technologies to realize this push or polling.
Is what you explained feasible on the user's side of happstack-server (I mean, if I can do it myself)? If I take an empty MVar in my ServerPartTs, which are read over client's request, I think that nothing will be sent back to the browser and it will remain blank! Is that to be combined with an HTTP refresh timer on the client side?
By sessions, you mean sessions that I create myself for every client connected?
Regards, Corentin
On Wed, Jan 19, 2011 at 2:21 PM, Bas van Dijk
wrote: On 17 January 2011 21:50, Jeremy Shaw
wrote: On Jan 17, 2011, at 2:19 PM, Corentin Dupont wrote:
Indeed, I tried with <META HTTP-EQUIV="Refresh" CONTENT="n"> ? and it's unusable. It make blink the page, ungrey the "stop" button for a second and make the fields loose the focus so it's impossible to type in.
I'll try with XMLHTTPRequest.
Right. Using the jQuery library should make it easier to do ajax requests and modify the DOM on the fly, http://jquery.com/ - jeremy _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
A nice variation of polling is "long polling":
http://en.wikipedia.org/wiki/Push_technology#Long_polling
This can easily be accomplished in Haskell by having an MVar per session. Initially an empty MVar is created per new session. When a client makes a request, the server thread that handles the request takes the MVar belonging to the session. This thread will block until the MVar is filled. When the server has an update it will fill all the MVars. This causes all the blocked threads to continue with sending a response to the client notifying it about the update.
Like Jeremy said it's a good idea to make these update requests asynchronous.
Regards,
Bas
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 19 January 2011 15:02, Corentin Dupont
Is what you explained feasible on the user's side of happstack-server (I mean, if I can do it myself)?
Yes, you can do it yourself.
If I take an empty MVar in my ServerPartTs, which are read over client's request, I think that nothing will be sent back to the browser and it will remain blank!
Yes, which is the intention. Here's a short incomplete and non type-checked snapshot of an application I'm currently developing which uses long-polling: Somewhere on the client you have the following Javascript which uses the JQuery library: update = function(){ $.ajax({url: "update", success: onUpdate}); } onUpdate = function(data){ // Update your page, possibly by making more ajax requests to the server... update(); } Now when you call update() the client will asynchronously request the server for updates. Each time there's an update the onUpdate function is run which gives you a chance to update your page. onUpdate will finally call update() again for the next iteration. Now on the server side you need something like this: main :: IO () main = do sessionStore <- newSessionStore forkIO (updateLoop sessionStore) simpleHTTP conf $ msum [ dir "update" (update sessionStore), ... ] update :: SessionStore -> ServerPartT IO Response update sessionStore = do mvar <- getSessionData sessionStore liftIO $ takeMVar mvar return someResponse getSessionData will check if there's an active session and if so will return the associated MVar. If there's no active session a new one will be created which will have an empty MVar associated with it. updateLoop is a loop that creates a new update and then notifies the clients: updateLoop :: SessionStore -> IO () updateLoop store = forever $ do makeNewUpdate notifySessions store notifySessions :: SessionStore -> IO () notifySessions store = do sessionData <- getAllSessionData store mapM_ (\mv -> tryPutMVar mv ()) sessionData Note that it's not necessary to have a separate thread for creating updates and notifying clients. You can just as well do this in one of your request handlers. I know the example is incomplete but I hope I got the essentials across.
Is that to be combined with an HTTP refresh timer on the client side?
No, as Thu already explain, no HTTP refresh timer is needed.
By sessions, you mean sessions that I create myself for every client connected?
Yes. However, I don't think sessions are required for long-polling. I just used them in my example because I used them in a similar way in an application I'm currently developing. Regards, Bas
participants (5)
-
Bas van Dijk
-
Chris Smith
-
Corentin Dupont
-
Jeremy Shaw
-
Vo Minh Thu