soegaard2
2020-2-22 08:36:54

@badkins Sounds like a useful concept. FWIW I dug around and found where Ruby stores the hash: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/flash.rb#L50 They have a key-value session into which the hash of the flash message is stored. I don’t know how they implement sessions.


popa.bogdanp
2020-2-22 18:51:53

popa.bogdanp
2020-2-22 19:12:33

The session id is stored in a cookie. Session data is stored in a session “store” (in memory, Redis, etc) and flash data is stored under the 'flash.messages key in the user’s session. The value of 'flash.messages is a list of symbol, string pairs. On (before) every request, the 'flash.messages key is popped off the session by this middleware.


philip.mcgrath
2020-2-22 20:27:08

I haven’t actually implemented “flash messages,” but I’ve thought about it. The unique aspect is that, AAUI, you don’t want the message to show if the user returns to the page with the browser’s back and forward buttons. That suggests that there’s some part of the implementation that’s going to have to be on the client side in JavaScript. It seems like you could communicate with a non-HttpOnly cookie that JavaScript unsets, or the history API might be enough to let you communicate the information in the URL but suppressing it on navigation. (I’ve never tried to use the history API that way, though.)


philip.mcgrath
2020-2-22 20:29:27

In general, I find the discussion of “page-local” data vs. “session” data vs. “global” data on p. 27 of the “Implementation and Use of the PLT Scheme Web Server” paper a very useful way of thinking: https://cs.brown.edu/~sk/Publications/Papers/Published/khmgpf-impl-use-plt-web-server-journal/paper.pdf


popa.bogdanp
2020-2-22 22:24:00

Generally, hitting back doesn’t reload the page, but, even if it did, I think the approach of “popping” the flash messages from session storage on page load doesn’t have this problem.

Take a post-login flash message as an example:

• the user successfully logs in by POSTing a form • the login handler sets a message and then redirects • the redirect target executes, pops the flashes from the session and renders the page


popa.bogdanp
2020-2-22 22:26:05

There is a very slight window in between when the redirect is returned and when the redirected request makes it to the server in which the user could conceivably hit the back button and run into problems after redirecting. If the server crashes between the two steps then that could also end up causing similar issues. That said, neither of these really end up being problems in practice IME.


philip.mcgrath
2020-2-22 22:32:48

It depends on how you implement “sessions.” If you’re maintaining server-side state, then yes, you’re right. (There’s another edge case if the user has multiple “sessions” simultaneously, but that’s not specific to flash messages.) If you are serializing state and transferring it the client (automatically or manually, by any of various means), then you have more issues.


philip.mcgrath
2020-2-22 22:35:34

And yes, “Back” may avoid reloading the page if the browser still has it cached, but it may also make a new request, and there are similar circumstances where reloading is more likely, like explicitly pressing “Refresh” or the browser restoring tabs at the beginning of a session.


philip.mcgrath
2020-2-22 22:37:25

Basically, a GET request should be idempotent, including in the last step of the “post/redirect/get” pattern.


philip.mcgrath
2020-2-22 22:43:17

On the other hand, for most types of flash messages, I think it isn’t actually a problem to have them in the history: like, it seems fine if the user can use “Back” to return to a flash message displaying an order confirmation. If vanishing on “Back” isn’t a constraint, the question becomes much easier. I would recommend web cells, as discussed in “Interaction-Safe State for the Web”: http://scheme2006.cs.uchicago.edu/03-mccarthy.pdf (and in the docs). I use them for Digital Ricoeur to redirect after log in, which in our case comes after emailing the user a one-shot link.


philip.mcgrath
2020-2-22 22:44:46

In our case, it’s very obvious if you’ve logged in or not, so an explicit confirmation message doesn’t seem necessary.


philip.mcgrath
2020-2-22 22:59:14

It’s a bit tangential, but a related part of our implementation is using preserved thread cells to hold per-request state. We have a dispatcher wrapper that deals with authenticating the user making the request. If the id cookie needs to be updated, the wrapper uses dynamic-wind to push the new cookie in the dynamic extent of the call to the inner servlet dispatcher, where it’s available to install in whatever response has to be generated, without having to write a bunch of boilerplate. I hope to have this code ready to make public soon, but I sketched it out in an old mailing list thread where Jay explained the problem with the first implementation I tried: https://groups.google.com/d/topic/racket-users/dvbI3sJE210/discussion


notjack
2020-2-22 23:09:43

hmm, how do session cookies interact with caching? if a page is cached, but you’re supposed to send some cookie when retrieving it and that cookie has been updated since the page was cached, what happens?


notjack
2020-2-22 23:09:53

oh right, the Vary header


philip.mcgrath
2020-2-22 23:29:16

In my case it’s ok if the cookie set is skipped (except for the POST requests to log in and log out, but those aren’t cached anyway). I use web-server/http/id-cookie, so the cookie has a (cryptographically signed) timestamp. If the timestamp is recent, I skip querying the database to make sure the session is still valid. I only need to reset the cookie when the (short) expiration has been exceeded, I’ve validated with the database again, and I have a newly-timestamped cookie. If it doesn’t get set for some reason, or if it gets set multiple times because of concurrent requests, that’s fine: it just means a few more trips to the database.


badkins
2020-2-22 23:52:31

I’ve been storing session data in a cookie which complicates matters since to clear the flash, you need to send an updated cookie. All of my web apps have an associated postgres database, so if I stored session state in the database, things become trivial. I’m not sure I want to require that in the framework I’m developing though, so I should probably create the mechanism to handle this with cookie session data.


badkins
2020-2-22 23:54:09

I should also mention that I’m operating in a stateless manner vs. making use of the stateful features of the Racket web server.


badkins
2020-2-23 00:00:18

… no continuations either …