From: avi@...4.com (Avi Bryant) Date: 2002-10-07T04:31:23+09:00 Subject: Re: CGI sessions as threads? Brian Candler wrote in message news:<20021006110515.A13624@uk.tiscali.com>... > Hello, > > One of the things which interests me most about Ruby is its built-in > threading capability. > > In particular, I would like to know if anyone has written a library which > allows CGI sessions to be modelled as threads? This would make it extremely > easy to write interactive CGI applications, as the thread would only see > exchanges between itself and its one client. Brian, I've done a lot of work on and with libraries very much like what you describe. My talk at RubyConf, in fact, will be on exactly this approach. Most of my work has been in Smalltalk, although I did spend some time this summer porting the basic ideas to Ruby; I'll be updating that port before my talk. If anyone is interested in taking a look at it, email me, but if you can wait until November it will be in much better shape. A few comments: > For example, you might start writing a simple login screen like this: > > ---------------------------------------------------------------------------- > retries = 0 > > begin > show 'login_page.htm' > u = wait_response > retries+=1 > end while retries < 3 && u.username!='brian' && u.password!='mypass' The explicit wait_response in unnecessary - this can just be begin u = show 'login_page.htm' retries += 1 end while retries < 3 && u.username!='brian' && u.password!='mypass' > wait_response blocks until the next CGI request (HTTP post) which is > associated with this particular thread arrives. It also commits suicide if > no response is received within an idle timeout period. > Perhaps for safety, the 'show' function should set a different cookie every > time, to ensure that the user cannot press 'back' multiple times and then > submit an out-of-sequence form - or else the server thread should have the > option of handling this case itself. Yes, exactly, although you need to encode this in the URL, not in a cookie, so that you can actually detect which page the user backtracked to. What I've found works best is this: - by default, if the user backtracks (or clones the browser window, or whatever), everything keeps working as before. If the user submits a form multiple times, *that call to show() simply returns multiple times*, and the thread proceeds from there. This is easy to do with ruby's callcc function, and is really what makes the whole approach so cool. - some times that's not appropiate; for that, you need to be able to set up "transactions" in the thread. For example, a piece of a shopping cart app might look like cart = confirm_cart_contents cc = ask_for_credit_card process_payment(cart, cc) show_thankyou_page You don't want a user to hit the back button and change the cart contents once the credit card has gone through! So you have something like this: session.isolate do cart = confirm_cart_contents cc = ask_for_credit_card process_payment(cart, cc) end show_thankyou_page While inside the "isolate" block, backtracking works normally (they can, for example, go back and change the cart). As soon as the "isolate" block ends, all of those pages immediately "expire", in that attempts to click any links or submit any forms from them will result in a redirect back to the thankyou page. > Anyway, it seems to me that this ought to be fairly straightforward to > implement, so has anybody done this already? I have a lot of Ruby learning > to do before I would attempt it myself :-) The core is indeed very straightforward to implement; it's probably about 40 lines of code. What's fun is all the great stuff you can build on top once you have this basic facility... Incidentally, as others have pointed out, this won't work as straight CGI; the expirements I've done in Ruby use WEBrick, but FastCGI would also work. Cheers, Avi