127 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Factor
		
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Factor
		
	
	
! Copyright (C) 2004 Chris Double.
 | 
						|
! Copyright (C) 2006 Slava Pestov.
 | 
						|
! See http://factorcode.org/license.txt for BSD license.
 | 
						|
USING: html http http.server.responders io kernel math
 | 
						|
namespaces prettyprint continuations random system sequences
 | 
						|
assocs ;
 | 
						|
IN: webapps.callback
 | 
						|
 | 
						|
#! Name of the variable holding the continuation used to exit
 | 
						|
#! back to the httpd responder.
 | 
						|
SYMBOL: exit-continuation 
 | 
						|
 | 
						|
#! Tuple to hold global request data. This gets passed to
 | 
						|
#! the continuation when resumed so it can restore things
 | 
						|
#! like 'stdio' so it writes to the correct socket. 
 | 
						|
TUPLE: request stream exitcc method url raw-query query header response ;
 | 
						|
 | 
						|
: <request> ( -- request )
 | 
						|
  stdio get
 | 
						|
  exit-continuation get
 | 
						|
  "method" get
 | 
						|
  "request" get
 | 
						|
  "raw-query" get
 | 
						|
  "query" get
 | 
						|
  "header" get
 | 
						|
  "response" get
 | 
						|
  request construct-boa ;
 | 
						|
 | 
						|
: restore-request ( -- )
 | 
						|
  request get 
 | 
						|
  dup request-stream stdio set 
 | 
						|
  dup request-method "method" set 
 | 
						|
  dup request-raw-query "raw-query" set 
 | 
						|
  dup request-query "query" set 
 | 
						|
  dup request-header "header" set 
 | 
						|
  dup request-response "response" set 
 | 
						|
  request-exitcc exit-continuation set ;
 | 
						|
 | 
						|
: update-request ( request new-request -- )
 | 
						|
  [ request-stream over set-request-stream ] keep 
 | 
						|
  [ request-method over set-request-method ] keep 
 | 
						|
  [ request-url over set-request-url ] keep 
 | 
						|
  [ request-raw-query over set-request-raw-query ] keep 
 | 
						|
  [ request-query over set-request-query ] keep 
 | 
						|
  [ request-header over set-request-header ] keep 
 | 
						|
  [ request-response over set-request-response ] keep 
 | 
						|
  request-exitcc swap set-request-exitcc ;
 | 
						|
  
 | 
						|
: with-exit-continuation ( quot -- ) 
 | 
						|
    #! Call the quotation with the variable exit-continuation bound 
 | 
						|
    #! such that when the exit continuation is called, computation 
 | 
						|
    #! will resume from the end of this 'with-exit-continuation' call. 
 | 
						|
    [ 
 | 
						|
        exit-continuation set call exit-continuation get continue
 | 
						|
    ] callcc0 drop ;
 | 
						|
 | 
						|
: expiry-timeout ( -- ms ) 900 1000 * ;
 | 
						|
 | 
						|
: get-random-id ( -- id ) 
 | 
						|
    #! Generate a random id to use for continuation URL's
 | 
						|
    4 big-random unparse ;
 | 
						|
 | 
						|
: callback-table ( -- <hashtable> ) 
 | 
						|
    #! Return the global table of continuations
 | 
						|
    \ callback-table get-global ;
 | 
						|
 | 
						|
: reset-callback-table ( -- ) 
 | 
						|
    #! Create the initial global table
 | 
						|
    H{ } clone \ callback-table set-global ;
 | 
						|
 | 
						|
reset-callback-table
 | 
						|
 | 
						|
#! Tuple for holding data related to a callback.
 | 
						|
TUPLE: item quot expire? request id  time-added ;
 | 
						|
 | 
						|
: <item> ( quot expire? request id -- item )
 | 
						|
    millis item construct-boa ;
 | 
						|
 | 
						|
: expired? ( item -- ? )
 | 
						|
    #! Return true if the callback item is expirable
 | 
						|
    #! and has expired (ie. was added to the table more than
 | 
						|
    #! timeout milliseconds ago).
 | 
						|
    [ item-time-added expiry-timeout + millis < ] keep
 | 
						|
    item-expire? and ;
 | 
						|
 | 
						|
: expire-callbacks ( -- )
 | 
						|
    #! Expire all continuations in the continuation table
 | 
						|
    #! if they are 'timeout-seconds' old (ie. were added
 | 
						|
    #! more than 'timeout-seconds' ago.
 | 
						|
    callback-table clone [
 | 
						|
        expired? [ callback-table delete-at ] [ drop ] if
 | 
						|
    ] assoc-each ;
 | 
						|
 | 
						|
: id>url ( id -- string )
 | 
						|
    #! Convert the continuation id to an URL suitable for
 | 
						|
    #! embedding in an HREF or other HTML.
 | 
						|
    "/responder/callback/?id=" swap url-encode append ;
 | 
						|
 | 
						|
: register-callback ( quot expire? -- url ) 
 | 
						|
    #! 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.  
 | 
						|
    request get get-random-id [ <item> ] keep
 | 
						|
    [ callback-table set-at ] keep
 | 
						|
    id>url ;
 | 
						|
 | 
						|
: register-html-callback ( quot expire? -- url )
 | 
						|
    >r [ serving-html ] swap append r> register-callback ;
 | 
						|
 | 
						|
: callback-responder ( -- )   
 | 
						|
    expire-callbacks
 | 
						|
    "id" query-param callback-table at [
 | 
						|
        [
 | 
						|
          dup item-request [
 | 
						|
            <request> update-request
 | 
						|
          ] when*
 | 
						|
          item-quot call 
 | 
						|
          exit-continuation get continue 
 | 
						|
        ] with-exit-continuation drop
 | 
						|
    ] [
 | 
						|
        "404 Callback not available" httpd-error
 | 
						|
    ] if* ;
 | 
						|
 | 
						|
global [
 | 
						|
    "callback" [ callback-responder ] add-simple-responder
 | 
						|
] bind
 |