factor/extra/webapps/continuation/continuation.factor

152 lines
5.6 KiB
Factor

! Copyright (C) 2004 Chris Double.
! See http://factorcode.org/license.txt for BSD license.
USING: http math namespaces io strings kernel html html.elements
hashtables continuations quotations parser generic sequences
webapps.callback http.server.responders ;
IN: webapps.continuation
#! Used inside the session state of responders to indicate whether the
#! next request should use the post-refresh-get pattern. It is set to
#! true after each request.
SYMBOL: post-refresh-get?
: >callable ( quot|interp|f -- interp )
dup continuation? [
[ continue ] curry
] when ;
: forward-to-url ( url -- )
#! When executed inside a 'show' call, this will force a
#! HTTP 302 to occur to instruct the browser to forward to
#! the request URL.
[
"HTTP/1.1 302 Document Moved\nLocation: " % %
"\nContent-Length: 0\nContent-Type: text/plain\n\n" %
] "" make write exit-continuation get continue ;
: forward-to-id ( id -- )
#! When executed inside a 'show' call, this will force a
#! HTTP 302 to occur to instruct the browser to forward to
#! the request URL.
>r "request" get r> id>url append forward-to-url ;
SYMBOL: current-show
: store-current-show ( -- )
#! Store the current continuation in the variable 'current-show'
#! so it can be returned to later by href callbacks. Note that it
#! recalls itself when the continuation is called to ensure that
#! it resets its value back to the most recent show call.
[ ( 0 -- )
[ ( 0 1 -- )
current-show set ( 0 -- )
continue
] callcc1
nip
restore-request
call
store-current-show
] callcc0 restore-request ;
: redirect-to-here ( -- )
#! Force a redirect to the client browser so that the browser
#! goes to the current point in the code. This forces an URL
#! change on the browser so that refreshing that URL will
#! immediately run from this code point. This prevents the
#! "this request will issue a POST" warning from the browser
#! and prevents re-running the previous POST logic. This is
#! known as the 'post-refresh-get' pattern.
post-refresh-get? get [
[
>callable t register-callback forward-to-url
] callcc0 restore-request
] [
t post-refresh-get? set
] if ;
: (show) ( quot -- hashtable )
#! See comments for show. The difference is the
#! quotation MUST set the content-type using 'serving-html'
#! or similar.
store-current-show redirect-to-here
[
>callable t register-callback swap with-scope
exit-continuation get continue
] callcc0 drop restore-request "response" get ;
: show ( quot -- namespace )
#! Call the quotation with the URL associated with the current
#! continuation. All output from the quotation goes to the client
#! browser. When the URL is later referenced then
#! computation will resume from this 'show' call with a hashtable on
#! the stack containing any query or post parameters.
#! 'quot' has stack effect ( url -- )
#! NOTE: On return from 'show' the stack is exactly the same as
#! initial entry with 'quot' popped off and the hashtable pushed on. Even
#! if the quotation consumes items on the stack.
[ serving-html ] swap append (show) ;
: (show-final) ( quot -- namespace )
#! See comments for show-final. The difference is the
#! quotation MUST set the content-type using 'serving-html'
#! or similar.
store-current-show redirect-to-here
with-scope exit-continuation get continue ;
: show-final ( quot -- namespace )
#! Similar to 'show', except the quotation does not receive the URL
#! to resume computation following 'show-final'. No continuation is
#! stored for this resumption. As a result, 'show-final' is for use
#! when a page is to be displayed with no further action to occur. Its
#! use is an optimisation to save having to generate and save a continuation
#! in that special case.
#! 'quot' has stack effect ( -- ).
[ serving-html ] swap compose (show-final) ;
#! Name of variable for holding initial continuation id that starts
#! the responder.
SYMBOL: root-callback
: cont-get/post-responder ( id-or-f -- )
#! httpd responder that handles the root continuation request.
#! The requests for actual continuation are processed by the
#! 'callback-responder'.
[
[ f post-refresh-get? set <request> request set root-callback get call ] with-scope
exit-continuation get continue
] with-exit-continuation drop ;
: quot-url ( quot -- url )
current-show get [ continue-with ] 2curry t register-callback ;
: quot-href ( text quot -- )
#! Write to standard output an HTML HREF where the href,
#! when referenced, will call the quotation and then return
#! back to the most recent 'show' call (via the callback-cc).
#! The text of the link will be the 'text' argument on the
#! stack.
<a quot-url =href a> write </a> ;
: install-cont-responder ( name quot -- )
#! Install a cont-responder with the given name
#! that will initially run the given quotation.
#!
#! Convert the quotation so it is run within a session namespace
#! and that namespace is initialized first.
[
[ cont-get/post-responder ] "get" set
[ cont-get/post-responder ] "post" set
swap "responder" set
root-callback set
] make-responder ;
: show-message-page ( message -- )
#! Display the message in an HTML page with an OK button.
[
"Press OK to Continue" [
swap paragraph
<a =href a> "OK" write </a>
] simple-page
] show 2drop ;