tidy up browser title.
put vocab and word name in browser titlebar. update cont-responder documentation. Added information on 'show-final' and updated examples that had suferred code rot (ie. Change use of times*, succ, pred, [ | ], etc).. remove setting of responder name in browser. The setting of the name caused the browser-edit responder to jump to the browser responder when a word was clicked.. added show-final to cont-responder. add responder-items word for debugging in cont-responder. changed expiry of cont-responder continuations. When the continuations are expired they are now removed from the hash table. Previously each request would rebuild the continuation table containing the non-expired continuations.. convert from hashtables to typles in cont-responder. move to using symbols for variable names in cont-responder. convert USE: to USING: in cont-responder. various minor cont-responder refactorings. refactor init-cont-responder. merge cont-get-responder and cont-put-responder into one. refactor cont-get-responder. fix cont-responder bug involving redirects. When a cont-responder request for an expired page was made the 'redirect' flag was not correctly reset. This resulted in initial requests of a responder redirecting when it wasn't supposed to. Tidied up the area of redirects and post-refresh-get pattern.. added use of show-final to todo-list.cvs
parent
ba492a60d8
commit
a0c7e80835
|
@ -106,7 +106,7 @@ USE: kernel
|
|||
|
||||
: todo-stylesheet-url ( -- url )
|
||||
#! Generate an URL for the stylesheet.
|
||||
t [ [ drop todo-stylesheet write ] show ] register-continuation id>url ;
|
||||
t [ [ todo-stylesheet write ] show-final ] register-continuation id>url ;
|
||||
|
||||
: include-todo-stylesheet ( -- )
|
||||
#! Generate HTML to include the todo stylesheet
|
||||
|
@ -476,14 +476,13 @@ USE: kernel
|
|||
[ "todo" get todo-username , "'s To Do list" , ] make-string
|
||||
[ include-todo-stylesheet ]
|
||||
[
|
||||
drop
|
||||
"todo" get write-item-table
|
||||
[
|
||||
[ "Add Item" [ do-add-new-item ] quot-href ]
|
||||
[ "Change Password" [ do-change-password ] quot-href ]
|
||||
] horizontal-layout
|
||||
] styled-page
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
: todo-example ( path -- )
|
||||
#! Startup the todo list example using the given path as the
|
||||
|
|
|
@ -37,14 +37,25 @@ gets run when the client accesses a particular URL. When run that word
|
|||
has 'standard output' bound in such a way that all output goes to the
|
||||
clients web browser.
|
||||
|
||||
In the 'cont-responder' system the word used to set output to go to the web
|
||||
browser and display a page is 'show'. Think of it as 'show a page to
|
||||
the client'. 'show' takes a single item on the stack and that is a
|
||||
'page generation' quotation.
|
||||
In the 'cont-responder' system there are two words used to set output
|
||||
to go to the web browser and display a page. They are 'show' and
|
||||
'show-final'. Think of them as 'show a page to the client'. 'show' and
|
||||
'show-final' both take a single item on the stack and that is a 'page
|
||||
generation' quotation.
|
||||
|
||||
A 'page generation' quotation is a quotation with stack effect
|
||||
( string -- ). For now we'll ignore the string it receives on the
|
||||
stack. Its purpose will be explained later.
|
||||
A 'page generation' quotation is a quotation which when called will
|
||||
output HTML to stdout. In the httpd system, stdout is bound to the
|
||||
socket connection to the clients web browser.
|
||||
|
||||
The 'page generation' quotation passed to 'show' should have stack
|
||||
effect ( string -- ) while that for 'show-final' has stack effect
|
||||
( -- ). The two words have very similar uses.
|
||||
|
||||
The big difference is with 'show'. It provides an URL to the page
|
||||
generation quotation that when requested will proceed with execution
|
||||
immediately following the 'show', and any POST request data will be on
|
||||
the stack. With 'show-final' no URL is passed so it is not possible to
|
||||
'resume' computation. This is explained more fully later.
|
||||
|
||||
Hello World 1
|
||||
=============
|
||||
|
@ -52,16 +63,12 @@ A simple 'hello world' responder would be:
|
|||
|
||||
: hello-world1 ( -- )
|
||||
[
|
||||
drop
|
||||
"<html><head><title>Hello World</title></head>" write
|
||||
"<body>Hello World!</body></html>" write
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
When installed this will show a single page which is simple HTML to
|
||||
display 'Hello World!'. The 'show' word returns a namespace, the
|
||||
purpose of which will also be explained later. For now we ignore it
|
||||
and drop it. Notice we also drop the 'URL' that the quotation passed
|
||||
to 'show' receives on the stack.
|
||||
display 'Hello World!'.
|
||||
|
||||
The responder is installed using:
|
||||
|
||||
|
@ -134,12 +141,11 @@ system:
|
|||
|
||||
: hello-world2 ( -- )
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Hello World" write </title> </head>
|
||||
<body> "Hello World!" write </body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
Install it using:
|
||||
|
||||
|
@ -149,14 +155,13 @@ Dynamic Data
|
|||
============
|
||||
|
||||
Adding dynamic data to the page is relatively easy. This example pulls
|
||||
information from the 'room' word which displays memory details about
|
||||
information from the 'room' word which returns memory details about
|
||||
the running Factor system. It also uses 'room.' which outputs these
|
||||
details to standard output and this is wrapped in a <pre> tag so it is
|
||||
formatted correctly.
|
||||
|
||||
: memory-stats1 ( -- )
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Memory Statistics" write </title> </head>
|
||||
<body>
|
||||
|
@ -181,7 +186,7 @@ formatted correctly.
|
|||
</body>
|
||||
<pre> room. </pre>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
"memorystats1" [ memory-stats1 ] install-cont-responder
|
||||
|
||||
|
@ -206,12 +211,11 @@ The HTML output can be refactored into different words. For example:
|
|||
|
||||
: memory-stats2 ( -- )
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Memory Statistics 2" write </title> </head>
|
||||
<body> room memory-stats-table 2drop </body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
"memorystats2" [ memory-stats2 ] install-cont-responder
|
||||
|
||||
|
@ -234,7 +238,7 @@ procedural flow is maintained.
|
|||
This brings us to the 'URL' stack item that is available to the 'page
|
||||
generation' quotation passed to 'show'. This URL is a string that
|
||||
contains an URL that can be embedded in the page. When the user access
|
||||
that URL computation is resumed from the point of the end of the
|
||||
that URL, computation is resumed from the point of the end of the
|
||||
'show' call as described above:
|
||||
|
||||
: flow-example1 ( -- )
|
||||
|
@ -257,21 +261,23 @@ that URL computation is resumed from the point of the end of the
|
|||
</html>
|
||||
] show drop
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Flow Example 1" write </title> </head>
|
||||
<body>
|
||||
<p> "Page 3" write </p>
|
||||
</body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
"flowexample1" [ flow-example1 ] install-cont-responder
|
||||
|
||||
The 'flow-example1' word contains three 'show' calls in a row. The
|
||||
first two display simple pages with an anchor link to the URL received
|
||||
on the stack. This URL when accessed resumes the computation. The
|
||||
final page just drops the URL.
|
||||
The 'flow-example1' word contains two 'show' calls in a row, followed
|
||||
by a 'show-final'. The 'show' calls display simple pages with an anchor
|
||||
link to the URL received on the stack. This URL when accessed resumes
|
||||
the computation. The final page doesn't require resumption of the
|
||||
computation so 'show-final' is used. We could have used 'show' and
|
||||
dropped the URL passed to the quotation and the result following the
|
||||
'show' but using 'show-final' is more efficient.
|
||||
|
||||
When you display this example in the browser you'll be able to click
|
||||
the URL to navigate. You can use the back button to retry the URL's,
|
||||
|
@ -301,12 +307,12 @@ this into a seperate word:
|
|||
|
||||
: flow-example2 ( n -- )
|
||||
#! Display the given number of pages in a row.
|
||||
dup pred [ succ t show-flow-page ] times*
|
||||
dup 1 - [ dup 1 + t show-flow-page ] repeat
|
||||
f show-flow-page ;
|
||||
|
||||
"flowexample2" [ 5 flow-example2 ] install-cont-responder
|
||||
|
||||
In this example the 'show-flow-age' pulls the page number off the
|
||||
In this example the 'show-flow-page' pulls the page number off the
|
||||
stack. It also gets whether or not to display the link to the next
|
||||
page.
|
||||
|
||||
|
@ -323,15 +329,16 @@ continuation which, when captured, had those items on the stack. The
|
|||
general rule of thumb is you will need to account for items on the
|
||||
stack before the show call.
|
||||
|
||||
This example also demonstrates using the 'times*' combinator to
|
||||
This example also demonstrates using the 'repeat' combinator to
|
||||
sequence the page shows. Any Factor code can be called and the
|
||||
continuation based system will sequentially display each page. The
|
||||
back button, browser window cloning, etc will all continue to work.
|
||||
|
||||
You'll notice the URL's in the browser have an 'id' query parameter with
|
||||
a number as its value. This is the 'continuation identifier' which is
|
||||
like a session id except that it identifies not just the data you have
|
||||
stored but your location within the responder as well.
|
||||
You'll notice the URL's in the browser have an 'id' query parameter
|
||||
with a sequence of characters as its value. This is the 'continuation
|
||||
identifier' which is like a session id except that it identifies not
|
||||
just the data you have stored but your location within the responder
|
||||
as well.
|
||||
|
||||
Forms and POST data
|
||||
===================
|
||||
|
@ -370,14 +377,13 @@ containing this data. Here is a simple example:
|
|||
|
||||
: post-example1 ( -- )
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Hello!" write </title> </head>
|
||||
<body>
|
||||
<p> accept-users-name write ", Good to see you!" write </p>
|
||||
</body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
"post-example1" [ post-example1 ] install-cont-responder
|
||||
|
||||
|
@ -398,23 +404,16 @@ the other way though:
|
|||
: post-example2 ( -- )
|
||||
accept-users-name
|
||||
[ ( name url -- )
|
||||
drop
|
||||
<html>
|
||||
<head> <title> "Hello!" write </title> </head>
|
||||
<body>
|
||||
<p> write ", Good to see you!" write </p>
|
||||
</body>
|
||||
</html>
|
||||
] show 2drop ;
|
||||
] show-final ;
|
||||
|
||||
"post-example2" [ post-example2 ] install-cont-responder
|
||||
|
||||
Either way works. Notice that in the 'post-example2' we had to do a
|
||||
'2drop' instead of a 'drop' at the end of the show to remove the
|
||||
additional 'name' that is on the stack. This wasn't needed in
|
||||
'post-example1' because the 'name' was not on the stack at the time of
|
||||
the 'show' call.
|
||||
|
||||
Associating URL's with words
|
||||
============================
|
||||
A web page can contain URL's that when clicked perform some
|
||||
|
@ -439,23 +438,19 @@ decremented.
|
|||
: counter-example1 ( - )
|
||||
#! Display a global counter which can be incremented or decremented
|
||||
#! using anchors.
|
||||
#!
|
||||
#! We don't need the 'url' argument
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head>
|
||||
<title> "Counter: " write "counter" get unparse dup write </title>
|
||||
</head>
|
||||
<body>
|
||||
<h2> "Counter: " write write </h2>
|
||||
<p> "++" [ "counter" get succ "counter" set ] quot-href
|
||||
"--" [ "counter" get pred "counter" set ] quot-href
|
||||
<p> "++" [ "counter" get 1 + "counter" set ] quot-href
|
||||
"--" [ "counter" get 1 - "counter" set ] quot-href
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show
|
||||
drop ;
|
||||
] show-final ;
|
||||
|
||||
"counter-example1" [ counter-example1 ] install-cont-responder
|
||||
|
||||
|
@ -495,20 +490,18 @@ rather than a namespace:
|
|||
|
||||
: counter-example2 ( count - )
|
||||
[ ( count URL -- )
|
||||
drop
|
||||
<html>
|
||||
<head>
|
||||
<title> "Counter: " write dup unparse write </title>
|
||||
</head>
|
||||
<body>
|
||||
<h2> "Counter: " write dup unparse write </h2>
|
||||
<p> "++" over [ succ counter-example2 ] cons quot-href
|
||||
"--" swap [ pred counter-example2 ] cons quot-href
|
||||
<p> "++" over [ 1 + counter-example2 ] cons quot-href
|
||||
"--" swap [ 1 - counter-example2 ] cons quot-href
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show
|
||||
drop ;
|
||||
] show-final ;
|
||||
|
||||
"counter-example2" [ 0 counter-example2 ] install-cont-responder
|
||||
|
||||
|
@ -517,8 +510,8 @@ to a code quotation that will increment or decrement it then call the
|
|||
responder again. So if the counter value is '5' the two 'quot-href'
|
||||
calls become the equivalent of:
|
||||
|
||||
"++" [ 5 succ counter-example2 ] cons quot-href
|
||||
"--" [ 5 pred counter-example2 ] cons quot-href
|
||||
"++" [ 5 1 + counter-example2 ] cons quot-href
|
||||
"--" [ 5 1 - counter-example2 ] cons quot-href
|
||||
|
||||
Because it calls itself with the new count value the state is
|
||||
remembered for that page only. This means that each page has an
|
||||
|
@ -547,7 +540,7 @@ very easy:
|
|||
|
||||
: show-some-pages ( n -- )
|
||||
#! Display the given number of pages in a row.
|
||||
[ succ show-page ] times* ;
|
||||
[ dup 1 + show-page ] repeat ;
|
||||
|
||||
: subroutine-example1 ( -- )
|
||||
[
|
||||
|
@ -563,7 +556,7 @@ very easy:
|
|||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
"subroutine-example1" [ subroutine-example1 ] install-cont-responder
|
||||
|
||||
|
@ -666,7 +659,7 @@ Again we skip past the forward:
|
|||
|
||||
Now we submit the post data along to the 'action' url:
|
||||
|
||||
"5456539333180428" [ [ "username" | "Chris" ] ] alist>hash test-cont-click
|
||||
"5456539333180428" [ [[ "username" "Chris" ]] ] alist>hash test-cont-click
|
||||
=>
|
||||
HTTP/1.0 200 Document follows
|
||||
Content-Type: text/html
|
||||
|
|
|
@ -78,7 +78,6 @@ errors unparser logging listener url-encoding hashtables memory ;
|
|||
: write-word-source ( vocab word -- )
|
||||
#! Write the source for the given word from the vocab as HTML.
|
||||
<namespace> [
|
||||
"responder" "browser" put
|
||||
"allow-edit?" get [ "Edit" [ "edit-state" t put ] quot-href <br/> ] when
|
||||
"edit-state" get [
|
||||
write-editable-word-source
|
||||
|
@ -187,7 +186,12 @@ errors unparser logging listener url-encoding hashtables memory ;
|
|||
[
|
||||
<html>
|
||||
<head>
|
||||
<title> "Factor Browser" write </title>
|
||||
<title>
|
||||
"Factor Browser - " write
|
||||
"current-vocab" get write
|
||||
" - " write
|
||||
"current-word" get write
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<form name= "main" action= method= "post" form>
|
||||
|
@ -212,7 +216,7 @@ errors unparser logging listener url-encoding hashtables memory ;
|
|||
[
|
||||
"vocabs" get dup [ ] [ drop "unknown" ] ifte "words" get dup [ ] [ drop "unknown" ] ifte browser-url
|
||||
forward-to-url
|
||||
] show
|
||||
] show-final
|
||||
] bind <browser> ;
|
||||
|
||||
: browser-responder ( allow-edit? -- )
|
||||
|
|
|
@ -21,25 +21,14 @@
|
|||
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
IN: cont-responder
|
||||
USE: stdio
|
||||
USE: httpd
|
||||
USE: httpd-responder
|
||||
USE: math
|
||||
USE: random
|
||||
USE: namespaces
|
||||
USE: streams
|
||||
USE: lists
|
||||
USE: strings
|
||||
USE: html
|
||||
USE: kernel
|
||||
USE: html
|
||||
USE: logging
|
||||
USE: url-encoding
|
||||
USE: unparser
|
||||
USE: hashtables
|
||||
USE: parser
|
||||
USE: prettyprint
|
||||
USE: inspector
|
||||
USING: stdio httpd httpd-responder math random namespaces streams
|
||||
lists strings kernel html url-encoding unparser hashtables
|
||||
parser generic ;
|
||||
|
||||
#! 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?
|
||||
|
||||
: expiry-timeout ( -- timeout-seconds )
|
||||
#! Number of seconds to timeout continuations in
|
||||
|
@ -49,22 +38,23 @@ USE: inspector
|
|||
#! testing.
|
||||
900 ;
|
||||
|
||||
: redirect-enabled?
|
||||
#! Set to true if you want the post-redirect-get pattern
|
||||
#! implemented. See the redirect-to-here word for details.
|
||||
t ;
|
||||
|
||||
: get-random-id ( -- id )
|
||||
#! Generate a random id to use for continuation URL's
|
||||
[ 32 [ random-digit unparse , ] times ] make-string str>number 36 >base ;
|
||||
|
||||
#! Name of variable holding the table of continuations.
|
||||
SYMBOL: table
|
||||
|
||||
: continuation-table ( -- <namespace> )
|
||||
#! Return the global table of continuations
|
||||
"continuation-table" get ;
|
||||
table get ;
|
||||
|
||||
: reset-continuation-table ( -- )
|
||||
#! Create the initial global table
|
||||
<namespace> "continuation-table" set ;
|
||||
<namespace> table set ;
|
||||
|
||||
#! Tuple for holding data related to a continuation.
|
||||
TUPLE: item expire? quot id time-added ;
|
||||
|
||||
: continuation-item ( expire? quot id -- <item> )
|
||||
#! A continuation item is the actual item stored
|
||||
|
@ -72,12 +62,7 @@ USE: inspector
|
|||
#! quotation/continuation, time added, etc. If
|
||||
#! expire? is true then the continuation will
|
||||
#! be expired after a certain amount of time.
|
||||
<namespace> [
|
||||
"id" set
|
||||
"quot" set
|
||||
"expire?" set
|
||||
millis "time-added" set
|
||||
] extend ;
|
||||
millis <item> ;
|
||||
|
||||
: seconds>millis ( seconds -- millis )
|
||||
#! Convert a number of seconds to milliseconds
|
||||
|
@ -87,9 +72,7 @@ USE: inspector
|
|||
#! Return true if the continuation item is expirable
|
||||
#! and has expired (ie. was added to the table more than
|
||||
#! timeout milliseconds ago).
|
||||
[ seconds>millis "time-added" get + millis - 0 <
|
||||
"expire?" get and
|
||||
] bind ;
|
||||
[ item-time-added swap seconds>millis + millis - 0 < ] keep item-expire? and ;
|
||||
|
||||
: continuation-items ( -- alist )
|
||||
#! Return an alist of all continuation items in the continuation
|
||||
|
@ -100,16 +83,31 @@ USE: inspector
|
|||
#! Expire all continuations in the continuation table
|
||||
#! if they are 'timeout-seconds' old (ie. were added
|
||||
#! more than 'timeout-seconds' ago.
|
||||
continuation-items [ cdr dupd expired? not ] subset nip
|
||||
alist>hash "continuation-table" set ;
|
||||
continuation-items [
|
||||
uncons pick swap expired? [
|
||||
continuation-table remove-hash
|
||||
] [
|
||||
drop
|
||||
] ifte
|
||||
] each drop ;
|
||||
|
||||
: expirable ( quot -- t quot )
|
||||
#! Set the stack up for a register-continuation call
|
||||
#! so that the given quotation is registered that it can
|
||||
#! be expired.
|
||||
t swap ;
|
||||
|
||||
: permanent ( quot -- f quot )
|
||||
#! Set the stack up for a register-continuation call
|
||||
#! so that the given quotation is never expired after
|
||||
#! registration.
|
||||
f swap ;
|
||||
|
||||
: register-continuation ( expire? quot -- id )
|
||||
#! Store a continuation in the table and associate it with
|
||||
#! a random id. That continuation will be expired after
|
||||
#! a certain period of time if 'expire?' is true.
|
||||
continuation-table [
|
||||
get-random-id -rot pick continuation-item over set
|
||||
] bind ;
|
||||
get-random-id -rot pick continuation-item over continuation-table set-hash ;
|
||||
|
||||
: append* ( lists -- list )
|
||||
#! Given a list of lists, append the lists together
|
||||
|
@ -123,13 +121,14 @@ USE: inspector
|
|||
|
||||
: get-continuation-item ( id -- <item> )
|
||||
#! Get the continuation item associated with the id.
|
||||
continuation-table [ get ] bind ;
|
||||
continuation-table hash ;
|
||||
|
||||
: id>url ( id -- string )
|
||||
#! Convert the continuation id to an URL suitable for
|
||||
#! embedding in an HREF or other HTML.
|
||||
url-encode "?id=" swap cat2 ;
|
||||
|
||||
DEFER: show-final
|
||||
DEFER: show
|
||||
|
||||
: expired-page-handler ( alist -- )
|
||||
|
@ -138,45 +137,52 @@ DEFER: show
|
|||
#! returning back to root continuation.
|
||||
drop
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<body>
|
||||
<p> "This page has expired." write </p>
|
||||
</body>
|
||||
</html>
|
||||
] show drop ;
|
||||
] show-final ;
|
||||
|
||||
: get-registered-continuation ( id -- cont )
|
||||
#! Return the continuation or quotation
|
||||
#! associated with the given id.
|
||||
#! TODO: handle expired pages better.
|
||||
expiry-timeout expire-continuations
|
||||
get-continuation-item dup [
|
||||
[ "quot" get ] bind
|
||||
get-continuation-item [
|
||||
item-quot
|
||||
] [
|
||||
drop [ expired-page-handler ]
|
||||
] ifte ;
|
||||
[ expired-page-handler ]
|
||||
] ifte* ;
|
||||
|
||||
: resume-continuation ( value id -- )
|
||||
#! Call the continuation associated with the given id,
|
||||
#! with 'value' on the top of the stack.
|
||||
get-registered-continuation call ;
|
||||
|
||||
#! Name of the variable holding the continuation used to exit
|
||||
#! back to the httpd responder, returning any generated HTML.
|
||||
SYMBOL: exit-cc
|
||||
|
||||
: exit-continuation ( -- exit )
|
||||
#! Get the current exit continuation
|
||||
"exit" get ;
|
||||
exit-cc get ;
|
||||
|
||||
: call-exit-continuation ( value -- )
|
||||
#! Call the exit continuation, passing it the given value on the
|
||||
#! top of the stack.
|
||||
"exit" get call ;
|
||||
exit-cc get call ;
|
||||
|
||||
: with-exit-continuation ( quot -- )
|
||||
#! Call the quotation with the variable "exit" bound such that when
|
||||
#! Call the quotation with the variable exit-cc bound such that when
|
||||
#! the exit continuation is called, computation will resume from the
|
||||
#! end of this 'with-exit-continuation' call, with the value passed
|
||||
#! to the exit continuation on the top of the stack.
|
||||
[ "exit" set call f call-exit-continuation ] callcc1 nip ;
|
||||
[ exit-cc set call f call-exit-continuation ] callcc1 nip ;
|
||||
|
||||
#! Name of variable holding the 'callback' continuation, used for
|
||||
#! returning back to previous 'show' calls.
|
||||
SYMBOL: callback-cc
|
||||
|
||||
: store-callback-cc ( -- )
|
||||
#! Store the current continuation in the variable 'callback-cc'
|
||||
|
@ -185,7 +191,7 @@ DEFER: show
|
|||
#! it resets its value back to the most recent show call.
|
||||
[ ( 0 -- )
|
||||
[ ( 0 1 -- )
|
||||
"callback-cc" set ( 0 -- )
|
||||
callback-cc set ( 0 -- )
|
||||
call
|
||||
] callcc1 ( 0 [ ] == )
|
||||
nip
|
||||
|
@ -215,15 +221,13 @@ DEFER: show
|
|||
#! "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.
|
||||
"disable-initial-redirect?" get [
|
||||
"disable-initial-redirect?" f put
|
||||
] [
|
||||
post-refresh-get? get [
|
||||
[
|
||||
t swap register-continuation
|
||||
[ "HTTP/1.1 302 Document Moved\nLocation: " , id>url , "\n" ,
|
||||
"Content-Length: 0\nContent-Type: text/plain\n\n" , ] make-string
|
||||
call-exit-continuation
|
||||
expirable register-continuation
|
||||
id>url forward-to-url
|
||||
] callcc1 drop
|
||||
] [
|
||||
t post-refresh-get? set
|
||||
] ifte ;
|
||||
|
||||
: show ( quot -- namespace )
|
||||
|
@ -235,51 +239,52 @@ DEFER: show
|
|||
#! NOTE: On return from 'show' the stack is exactly the same as
|
||||
#! initial entry with 'quot' popped off an <namespace> put on. Even
|
||||
#! if the quotation consumes items on the stack.
|
||||
store-callback-cc
|
||||
redirect-enabled? [ redirect-to-here ] when
|
||||
store-callback-cc redirect-to-here
|
||||
[
|
||||
t swap register-continuation id>url swap
|
||||
[ serving-html ] car swons with-string-stream
|
||||
call-exit-continuation
|
||||
expirable register-continuation id>url swap
|
||||
\ serving-html swons with-string-stream call-exit-continuation
|
||||
] callcc1
|
||||
nip ;
|
||||
|
||||
: 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.
|
||||
store-callback-cc redirect-to-here
|
||||
\ serving-html swons with-string-stream call-exit-continuation ;
|
||||
|
||||
: cont-get-responder ( id-or-f -- )
|
||||
#! Name of variable for holding initial continuation id that starts
|
||||
#! the responder.
|
||||
SYMBOL: root-continuation
|
||||
|
||||
: id-or-root ( -- id )
|
||||
#! Return the continuation id for the current requested continuation
|
||||
#! or the root continuation if no id is supplied.
|
||||
"id" "query" get assoc [ root-continuation get ] unless* ;
|
||||
|
||||
: cont-get/post-responder ( id-or-f -- )
|
||||
#! httpd responder that retrieves a continuation and calls it.
|
||||
drop
|
||||
"id" "query" get assoc
|
||||
dup f-or-"" [
|
||||
#! No continuation id given
|
||||
drop "root-continuation" get dup [
|
||||
#! Use the root continuation
|
||||
[ f swap resume-continuation ] with-exit-continuation
|
||||
] [
|
||||
#! No root continuation either
|
||||
drop [ f expired-page-handler ] with-exit-continuation
|
||||
] ifte
|
||||
] [
|
||||
#! Use the given continuation
|
||||
[ f swap resume-continuation ] with-exit-continuation
|
||||
] ifte
|
||||
[ write flush ] when* drop ;
|
||||
|
||||
: cont-post-responder ( id -- )
|
||||
#! httpd responder that retrieves a continuation for the given
|
||||
#! id and calls it with the POST data as a hashtable on the top
|
||||
#! of the stack.
|
||||
[
|
||||
drop
|
||||
#! The continuation id must be in a query parameter called 'id'.
|
||||
#! If it does not exist the root continuation is called. If
|
||||
#! no root continuation exists the expired continuation handler
|
||||
#! should be called.
|
||||
drop [
|
||||
"response" get alist>hash
|
||||
"id" "query" get assoc resume-continuation
|
||||
] with-exit-continuation
|
||||
print drop ;
|
||||
id-or-root [
|
||||
resume-continuation
|
||||
] [
|
||||
expired-page-handler
|
||||
] ifte*
|
||||
] with-exit-continuation [ write flush ] when* ;
|
||||
|
||||
: callback-quot ( quot -- quot )
|
||||
#! Convert the given quotation so it works as a callback
|
||||
#! by returning a quotation that will pass the original
|
||||
#! quotation to the callback continuation.
|
||||
unit "callback-cc" get [ call ] cons append ;
|
||||
[ , callback-cc get , \ call , ] make-list ;
|
||||
|
||||
: quot-href ( text quot -- )
|
||||
#! Write to standard output an HTML HREF where the href,
|
||||
|
@ -287,20 +292,16 @@ DEFER: show
|
|||
#! 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 href= callback-quot t swap register-continuation id>url a> write </a> ;
|
||||
|
||||
: with-new-session ( quot -- )
|
||||
#! Each cont-responder is bound inside their own
|
||||
#! namespace for storing session state. Run the given
|
||||
#! quotation inside a new namespace for this purpose.
|
||||
<namespace> swap bind ;
|
||||
<a href= callback-quot expirable register-continuation id>url a> write </a> ;
|
||||
|
||||
: init-session-namespace ( -- )
|
||||
#! Setup the initial session namespace. Currently this only
|
||||
#! copies the global value of whether the initial redirect
|
||||
#! will be disabled. It assumes the session namespace is
|
||||
#! topmost on the namespace stack.
|
||||
"disable-initial-redirect?" get "disable-initial-redirect?" set ;
|
||||
#! sets the redirect flag so that the initial request of the
|
||||
#! responder will not do a post-refresh-get style redirect.
|
||||
#! This prevents the initial request to a responder from redirecting
|
||||
#! to an URL with a continuation id. This word must be run from
|
||||
#! within the session namespace.
|
||||
f post-refresh-get? set ;
|
||||
|
||||
: install-cont-responder ( name quot -- )
|
||||
#! Install a cont-responder with the given name
|
||||
|
@ -308,20 +309,21 @@ DEFER: show
|
|||
#!
|
||||
#! Convert the quotation so it is run within a session namespace
|
||||
#! and that namespace is initialized first.
|
||||
[ init-session-namespace ] swap append unit [ with-new-session ] append
|
||||
"httpd-responders" get [
|
||||
\ init-session-namespace swons [ , \ with-scope , ] make-list
|
||||
<responder> [
|
||||
[ cont-get-responder ] "get" set
|
||||
[ cont-post-responder ] "post" set
|
||||
[ cont-get/post-responder ] "get" set
|
||||
[ cont-get/post-responder ] "post" set
|
||||
over "responder-name" set
|
||||
over "responder" set
|
||||
reset-continuation-table
|
||||
"disable-initial-redirect?" t put
|
||||
] extend dup >r rot set
|
||||
r> [
|
||||
f swap register-continuation "root-continuation" set
|
||||
] bind
|
||||
] bind ;
|
||||
permanent register-continuation root-continuation set
|
||||
] extend swap "httpd-responders" get set-hash ;
|
||||
|
||||
: responder-items ( name -- items )
|
||||
#! Return the table of continuation items for a given responder.
|
||||
#! Useful for debugging.
|
||||
"httpd-responders" get hash [ continuation-table ] bind ;
|
||||
|
||||
|
||||
: simple-page ( title quot -- )
|
||||
#! Call the quotation, with all output going to the
|
||||
|
|
Loading…
Reference in New Issue