2003-01-19 11:57:27 -05:00
;; the surflet handler
2002-09-13 03:21:19 -04:00
;; Copyright Andreas Bernauer, 2002
2002-09-30 03:53:00 -04:00
( define *debug* #t )
2002-09-29 11:20:36 -04:00
2003-01-19 11:57:27 -05:00
;;; session-table: entry for every new request on a surflet page
2002-12-07 17:26:40 -05:00
( define-record-type session :session
2003-01-19 11:57:27 -05:00
( make-session surflet-name memo
2002-09-29 11:20:36 -04:00
continuation-table continuation-table-lock
2002-10-02 20:15:44 -04:00
continuation-counter
2003-01-19 11:57:27 -05:00
surflet-data )
2002-12-07 17:26:40 -05:00
session?
2003-01-19 11:57:27 -05:00
( surflet-name session-surflet-name )
2002-12-07 17:26:40 -05:00
( memo session-memo set-session-memo! )
( continuation-table session-continuation-table )
( continuation-table-lock session-continuation-table-lock )
( continuation-counter session-continuation-counter )
2003-01-19 11:57:27 -05:00
( surflet-data session-surflet-data set-session-surflet-data! ) )
2002-09-13 03:21:19 -04:00
2002-10-21 04:25:58 -04:00
( define-record-type memo :memo
( make-memo message value new-memo )
memo?
( message memo:message set-memo:message ) ;kill, killed, adjust-timeout
( value memo:value set-memo:value )
( new-memo memo:new-memo set-memo:new-memo ) )
( define ( make-default-memo )
( make-memo 'kill #f #f ) )
2002-09-29 11:20:36 -04:00
2002-10-01 08:08:42 -04:00
;; caller must do locking stuff
( define ( memo-killed! memo )
( set-memo:message memo 'killed ) )
2002-12-07 17:26:40 -05:00
( define-record-type instance :instance
( make-instance session-id return-continuation )
instance?
( session-id really-instance-session-id
set-instance-session-id! )
( return-continuation really-instance-return-continuation
set-instance-return-continuation! ) )
2002-09-13 03:21:19 -04:00
2002-10-21 04:25:58 -04:00
( define-record-type options :options
2003-01-19 11:57:27 -05:00
( make-options surflet-path surflet-prefix cache-surflets? session-lifetime )
2002-10-21 04:25:58 -04:00
options?
2003-01-19 11:57:27 -05:00
( surflet-path options:surflet-path set-options:surflet-path )
( surflet-prefix options:surflet-prefix set-options:surflet-prefix )
( cache-surflets? options:cache-surflets? set-options:cache-surflets? )
2002-12-07 17:26:40 -05:00
;; session lifetime is in seconds
( session-lifetime options:session-lifetime set-options:session-lifetime ) )
2002-10-21 04:25:58 -04:00
2003-01-24 10:23:51 -05:00
;;; STATUS is the status code, an exact integer. See httpd/response.scm
;;; e.g. (status-code ok)
;;; CONTENT-TYPE is a string, most probably "text/html".
;;; HEADERS is a (maybe empty) list of pairs of (string or symbol);
;;; Additional headers to send, e.g. '(("Cache-Control" . "no-cache")) or
;;; '((Cache-Control . "no-cache")) etc.
;;; DATA is either
;;; * a string
;;; * a list of strings
;;; This list maybe extended to vectors later.
( define-record-type surflet-response :surflet-response
( make-surflet-response status content-type headers data )
surflet-response?
( status surflet-response-status )
( content-type surflet-response-content-type )
( headers surflet-response-headers )
( data surflet-response-data ) )
( define ( valid-surflet-response-data? data )
( or ( string? data ) ( list? data ) ) )
2003-01-19 11:57:27 -05:00
;; Surflet-prefix is unused now. Formerly, it contained the virtual
2002-12-02 03:42:37 -05:00
;; path prefix for the handler.
2003-01-19 11:57:27 -05:00
( define ( make-default-options surflet-path surflet-prefix )
( make-options surflet-path surflet-prefix #t 600 ) )
2002-09-29 09:43:39 -04:00
2002-10-04 11:51:04 -04:00
( define *options* ( make-preserved-thread-fluid #f ) )
;; preserved thread fluid because between different calls to
2003-01-19 11:57:27 -05:00
;; surflet-handler the options shall remain the same.
2002-09-29 09:43:39 -04:00
2002-10-01 13:39:39 -04:00
( define ( make-fluid-selector selector )
2002-10-04 11:51:04 -04:00
( lambda ( ) ( selector ( thread-fluid *options* ) ) ) )
2002-10-01 13:39:39 -04:00
( define ( make-fluid-setter setter )
( lambda ( value )
2002-10-04 11:51:04 -04:00
( setter ( thread-fluid *options* ) value ) ) )
2003-01-19 11:57:27 -05:00
( define options-surflet-path ( make-fluid-selector options:surflet-path ) )
( define options-surflet-prefix ( make-fluid-selector options:surflet-prefix ) )
( define options-cache-surflets? ( make-fluid-selector options:cache-surflets? ) )
2002-12-07 17:26:40 -05:00
( define options-session-lifetime ( make-fluid-selector options:session-lifetime ) )
2003-01-19 11:57:27 -05:00
( define set-options-cache-surflets? ( make-fluid-setter set-options:cache-surflets? ) )
2002-12-07 17:26:40 -05:00
( define set-options-session-lifetime ( make-fluid-setter set-options:session-lifetime ) )
2002-10-01 13:39:39 -04:00
2002-12-07 17:26:40 -05:00
( define *session-table* ( make-integer-table ) ) ; session-id is index
( define *session-table-lock* ( make-lock ) )
2002-09-29 09:43:39 -04:00
2002-09-18 11:32:41 -04:00
( define random
( let* ( ( source ( make-random-source ) )
( random-integer ( begin
( random-source-randomize! source )
( random-source-make-integers source ) ) ) )
( lambda ( )
( random-integer 1073741824 ) ) ) ) ; I hope, 1+ billion is enough....
2002-09-13 03:21:19 -04:00
2003-01-19 11:57:27 -05:00
( define ( surflet-handler surflet-path )
( set-thread-fluid! *options* ( make-default-options surflet-path #f ) )
2002-09-13 03:21:19 -04:00
( lambda ( path req )
( if ( pair? path ) ; need at least one element
2002-11-29 09:49:22 -05:00
( let ( ( request-method ( request-method req ) )
2003-01-14 10:01:21 -05:00
( path-string ( uri-path->uri path ) ) )
2002-10-04 11:56:58 -04:00
( if ( or ( string=? request-method "GET" )
( string=? request-method "POST" ) )
2002-11-05 05:21:15 -05:00
( if ( resume-url? path-string )
2003-01-19 11:57:27 -05:00
( resume-url path-string surflet-path req )
( launch-new-session path-string surflet-path req ) )
2003-01-10 04:52:35 -05:00
( make-error-response ( status-code method-not-allowed ) req
2002-10-04 11:56:58 -04:00
request-method ) ) )
2003-01-10 04:52:35 -05:00
( make-error-response ( status-code bad-request ) req
2002-09-13 03:21:19 -04:00
( format #f "Bad path: ~s" path ) ) ) ) )
2003-01-19 11:57:27 -05:00
( define ( launch-new-session path-string surflet-path req )
2002-10-02 19:47:07 -04:00
( cond
2003-01-19 11:57:27 -05:00
( ( file-not-exists? ( absolute-file-name path-string surflet-path ) )
2003-01-10 04:52:35 -05:00
( make-error-response ( status-code not-found ) req path-string ) )
2002-10-02 19:47:07 -04:00
( ( string=? ( file-name-extension path-string ) ".scm" )
2002-12-07 17:26:40 -05:00
( obtain-lock *session-table-lock* )
;; no access to session table until new session-id is saved
( let ( ( session-id ( generate-new-table-id *session-table* ) )
2002-10-21 04:25:58 -04:00
( memo ( make-default-memo ) ) )
2002-12-07 17:26:40 -05:00
( table-set! *session-table* session-id
( make-session path-string ; used to make
2002-10-02 20:45:41 -04:00
; redirections to origin
2002-10-02 19:47:07 -04:00
memo
( make-integer-table ) ; continuation table
( make-lock ) ; continuation table lock
2002-10-02 20:45:41 -04:00
( make-thread-safe-counter ) ; continuation counter
2003-01-19 11:57:27 -05:00
#f ) ) ; surflet-data
2002-12-07 17:26:40 -05:00
( release-lock *session-table-lock* )
( register-instance! session-id 'no-return )
2002-11-05 05:21:15 -05:00
( with-fatal-handler
2003-01-19 11:57:27 -05:00
;; Catch conditions from get-surflet-rt-structure.
2002-11-05 05:21:15 -05:00
( lambda ( condition decline )
2002-12-07 17:26:40 -05:00
( delete-session! session-id )
2002-11-05 05:21:15 -05:00
( bad-gateway-error-response req path-string condition ) )
2003-01-19 11:57:27 -05:00
( let ( ( surflet ( get-surflet-rt-structure path-string surflet-path ) ) )
2002-11-05 05:21:15 -05:00
( fork-thread
2002-12-07 17:26:40 -05:00
( session-surveillance session-id
( + ( time ) ( options-session-lifetime ) )
2002-11-05 05:21:15 -05:00
memo ) )
( reset
2002-11-09 13:25:20 -05:00
( with-fatal-handler
2003-01-19 11:57:27 -05:00
;; Catch conditions that occur while running the surflet.
2002-11-09 13:25:20 -05:00
( lambda ( condition decline )
2002-12-07 17:26:40 -05:00
( delete-session! session-id )
2002-11-09 13:25:20 -05:00
;; Restore correct continuation with shift.
( shift unused
( bad-gateway-error-response req path-string condition ) ) )
2003-01-19 11:57:27 -05:00
( with-cwd surflet-path
2002-11-09 13:25:20 -05:00
( with-names-from-rt-structure
2003-01-19 11:57:27 -05:00
surflet surflet-interface
2002-12-07 17:26:40 -05:00
( main req ) ) ) ) ) ) ) ) ) ; Launch serlvet's main procedure.
2002-10-02 19:47:07 -04:00
( else ; We'll serve every non-scm file.
;; We need access to SEND-FILE-RESPONSE of
;; HTTPD-FILE-DIR-HANDLERS. In the official SUnet release, we
;; don't have it, so we disable this feature here.
2003-01-19 11:57:27 -05:00
; (let ((full-file-name (absolute-file-name path-string surflet-path)))
2002-10-02 19:47:07 -04:00
; (send-file-response full-file-name
; (file-info full-file-name)
; req))
2003-01-10 04:52:35 -05:00
( make-error-response ( status-code forbidden ) req
2002-10-02 19:47:07 -04:00
"Can't serve other than Scheme files."
path-string ) )
) )
2002-09-13 03:21:19 -04:00
2002-12-07 17:26:40 -05:00
( define ( session-surveillance session-id time-to-die memo )
2002-09-29 11:20:36 -04:00
( lambda ( )
( let loop ( ( time-to-die time-to-die )
( memo memo ) )
2002-12-07 17:26:40 -05:00
( debug "session-surveillance[~s]: going to sleep until ~a"
session-id ( format-date "~c" ( date time-to-die ) ) )
2002-09-29 11:20:36 -04:00
( let ( ( seconds-to-sleep ( - time-to-die ( time ) ) ) )
( if ( positive? seconds-to-sleep )
( sleep ( * 1000 seconds-to-sleep ) ) ) )
;; check state of the world
( case ( memo:message memo )
( ( killed ) ; too late
2002-12-07 17:26:40 -05:00
( debug "session-surveillance[~s]: session already killed, dieing"
session-id )
2002-09-29 11:20:36 -04:00
)
( ( adjust-timeout ) ; new timeout
2002-12-07 17:26:40 -05:00
( debug "session-surveillance[~s]: adjusting timeout" session-id )
2002-09-29 11:20:36 -04:00
( loop ( memo:value memo )
( memo:new-memo memo ) ) )
2002-12-07 17:26:40 -05:00
( ( kill ) ; kill session
( debug "session-surveillance[~s]: killing"
session-id )
( obtain-lock *session-table-lock* )
( table-set! *session-table* session-id #f )
( release-lock *session-table-lock* ) )
2002-09-29 11:20:36 -04:00
( else
( format ( current-error-port )
2002-12-07 17:26:40 -05:00
"session-surveillance[~s]: unknown message ~s; dieing"
session-id ( memo:message memo ) ) ) ) ) ) )
2002-09-29 11:20:36 -04:00
2002-09-13 03:21:19 -04:00
;; try to get continuation-table and then the continuation
2002-09-21 16:18:49 -04:00
( define resume-url
( let ( ( bad-request
2002-09-26 08:13:01 -04:00
( lambda ( path-string req )
2003-01-10 04:52:35 -05:00
( make-error-response
2003-01-09 10:05:30 -05:00
( status-code bad-request ) req
2002-09-30 03:53:00 -04:00
( format #f
" <br>
2003-01-19 11:57:27 -05:00
<p>There may be several reasons, why your request for a surflet was denied:
2002-09-30 03:53:00 -04:00
<ul>
2003-01-19 11:57:27 -05:00
<li>The surflet does not accept any requests any more . </li>
<li>The surflet URL has timed out . </li>
2002-09-30 03:53:00 -04:00
<li>You URL is illformed . </li>
</ul>
</p>
2003-01-19 11:57:27 -05:00
<p>In any case, you may try to restart the surflet from the <a href= \ "~a\">beginning</a>. Your browser may also have cached an old session of this surflet. In this case, try to reload the page.</p>"
( resume-url-surflet-name path-string ) ) ) ) )
2002-09-21 16:18:49 -04:00
( lookup-continuation-table
2002-12-07 17:26:40 -05:00
( lambda ( session continuation-table continuation-id )
( let ( ( continuation-table-lock ( session-continuation-table-lock session ) ) )
2002-09-21 16:18:49 -04:00
( obtain-lock continuation-table-lock )
( let ( ( result ( table-ref continuation-table continuation-id ) ) )
( release-lock continuation-table-lock )
result ) ) ) ) )
2003-01-19 11:57:27 -05:00
( lambda ( path-string surflet-path req )
2002-12-07 17:26:40 -05:00
( receive ( session-id continuation-id )
2002-09-26 08:13:01 -04:00
( resume-url-ids path-string )
2002-12-07 17:26:40 -05:00
( let ( ( session ( session-lookup session-id ) ) )
( if session
( let* ( ( continuation-table ( session-continuation-table session ) )
( resume ( lookup-continuation-table session continuation-table
2002-09-21 16:18:49 -04:00
continuation-id ) ) )
( if resume
2003-01-19 11:57:27 -05:00
( with-cwd surflet-path
2002-12-07 17:26:40 -05:00
( reset
( begin
( register-instance! session-id 'no-return )
( resume req ) ) ) )
2002-09-26 08:13:01 -04:00
( bad-request path-string req ) ) )
( bad-request path-string req ) ) )
2002-09-24 04:47:33 -04:00
) ) ) )
2002-09-29 11:20:36 -04:00
2002-09-21 16:18:49 -04:00
2003-01-24 10:23:51 -05:00
;; RESPONSE-MAKER is a procedure returnig a SURFLET-RESPONSE.
2002-09-24 04:15:21 -04:00
( define ( send/suspend response-maker )
2002-09-14 12:42:24 -04:00
( shift return
2002-12-07 17:26:40 -05:00
( let* ( ( session-id ( instance-session-id ) )
( session ( session-lookup session-id ) ) )
;; the instance might be deleted in the meanwhile
( if session
2002-10-01 08:08:42 -04:00
( begin
2002-12-07 17:26:40 -05:00
( session-adjust-timeout! session-id )
( let ( ( continuations-table ( session-continuation-table session ) )
( continuation-table-lock ( session-continuation-table-lock session ) )
( continuation-counter ( session-next-continuation-counter session ) ) )
2002-10-01 08:08:42 -04:00
( obtain-lock continuation-table-lock )
( let ( ( continuation-id ( generate-new-table-id continuations-table ) ) )
( table-set! continuations-table continuation-id return )
( release-lock continuation-table-lock )
2003-01-19 11:57:27 -05:00
( let ( ( new-url ( make-resume-url ( session-surflet-name session )
2002-12-07 17:26:40 -05:00
session-id
2002-10-01 08:08:42 -04:00
continuation-counter
continuation-id ) ) )
2003-01-24 10:23:51 -05:00
( make-http-response ( response-maker new-url ) ) ) ) ) )
2003-01-10 04:52:35 -05:00
( make-error-response ( status-code not-found ) #f
2003-01-24 10:23:51 -05:00
"The URL refers to a SUrflet, whose session is no longer alive." ) ) ) ) )
2002-09-13 03:21:19 -04:00
2002-09-24 04:15:21 -04:00
( define ( send/finish response )
2002-12-07 17:26:40 -05:00
( delete-session! ( instance-session-id ) )
2003-01-24 10:23:51 -05:00
( shift unused ( make-http-response response ) ) )
2002-09-24 05:01:26 -04:00
( define ( send response )
2003-01-24 10:23:51 -05:00
( shift unused ( make-http-response response ) ) )
2002-09-13 03:21:19 -04:00
2003-01-24 11:02:29 -05:00
;; REQ may be #f in some (most) cases.
( define ( send-error status-code req . messages )
( shift unused ( apply make-error-response
( cons status-code
( cons #f messages ) ) ) ) )
2003-01-24 10:23:51 -05:00
( define ( make-http-response response )
( cond
( ( surflet-response? response )
( let ( ( data ( surflet-response-data response ) ) )
( if ( valid-surflet-response-data? data )
2003-01-14 06:27:42 -05:00
( make-response
2003-01-24 10:23:51 -05:00
( surflet-response-status response )
#f
( time )
( surflet-response-content-type response )
( surflet-response-headers response )
( make-writer-body
2003-01-14 06:27:42 -05:00
( lambda ( out options )
2003-01-24 10:23:51 -05:00
( cond
( ( string? data ) ( display data out ) )
( ( list? data ) ( for-each ( lambda ( data ) ( display data out ) ) data ) )
( else ;; We lose.
( display "Error in SUrflet output.\n" out ) )
) ) ) )
( make-error-response ( status-code bad-gateway ) #f
"The SUrflet returned an invalid response object (no surflet-response)." ) ) ) )
( ( and ( response? response ) ;; RESPONSE? refers to a HTTP-RESPONSE.
( redirect-body? ( response-body response ) ) )
response )
( else
( make-error-response ( status-code bad-gateway ) #f
"The SUrflet returned an invalid response object (no surflet-response)." ) ) ) )
2003-01-14 06:27:42 -05:00
2002-09-13 03:21:19 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2002-12-07 17:26:40 -05:00
;; access to session-table
( define ( session-lookup session-id )
( obtain-lock *session-table-lock* )
( let ( ( result ( table-ref *session-table* session-id ) ) )
( release-lock *session-table-lock* )
2002-09-21 16:18:49 -04:00
result ) )
2002-12-07 17:26:40 -05:00
( define ( session-next-continuation-counter session )
2002-09-21 16:18:49 -04:00
( thread-safe-counter-next!
2002-12-07 17:26:40 -05:00
( session-continuation-counter session ) ) )
2002-09-21 16:18:49 -04:00
2002-12-07 17:26:40 -05:00
( define ( delete-session! session-id )
( obtain-lock *session-table-lock* )
;; notify surveillance of session being alread killed (prevents
;; surveillance of killing new session that has the same number by
2002-09-29 11:20:36 -04:00
;; accident)
2002-12-07 17:26:40 -05:00
( let ( ( session ( table-ref *session-table* session-id ) ) )
( memo-killed! ( session-memo session ) ) )
2002-09-21 16:18:49 -04:00
;; why can't table entries be deleted correctly?
2002-12-07 17:26:40 -05:00
( table-set! *session-table* session-id #f )
( release-lock *session-table-lock* ) )
2002-09-21 16:18:49 -04:00
2002-12-07 17:26:40 -05:00
( define ( session-adjust-timeout! session-id )
( obtain-lock *session-table-lock* )
( let* ( ( session ( table-ref *session-table* session-id ) )
( memo ( session-memo session ) )
2002-10-21 04:25:58 -04:00
( new-memo ( make-default-memo ) ) )
2002-09-29 11:20:36 -04:00
;; Do it this way: new values and then new message
( set-memo:value memo
( + ( time )
2002-12-07 17:26:40 -05:00
( options-session-lifetime ) ) )
2002-09-29 11:20:36 -04:00
( set-memo:new-memo memo new-memo )
2002-09-30 03:53:00 -04:00
;; I don't think we need locking here. Do you agree?
2002-12-07 17:26:40 -05:00
( set-session-memo! session new-memo )
2002-09-29 11:20:36 -04:00
( set-memo:message memo 'adjust-timeout ) )
2002-12-07 17:26:40 -05:00
( release-lock *session-table-lock* ) )
2002-09-29 11:20:36 -04:00
2002-12-07 17:26:40 -05:00
;; adjusts the timeout of the current session
2002-10-21 04:25:58 -04:00
( define ( adjust-timeout )
2002-12-07 17:26:40 -05:00
( session-adjust-timeout! ( instance-session-id ) ) )
2002-10-21 04:25:58 -04:00
2002-12-07 17:26:40 -05:00
( define ( reset-session-table! )
2002-09-29 11:20:36 -04:00
( with-fatal-error-handler
( lambda ( condtion decline )
2002-12-07 17:26:40 -05:00
( release-lock *session-table-lock* )
2002-09-29 11:20:36 -04:00
( decline ) )
( lambda ( )
2002-12-07 17:26:40 -05:00
( obtain-lock *session-table-lock* )
;; notify session killing
2002-09-29 11:20:36 -04:00
( table-walk
2002-12-07 17:26:40 -05:00
( lambda ( session-id session )
( memo-killed! ( session-memo session ) ) )
*session-table* )
( set! *session-table* ( make-integer-table ) )
( release-lock *session-table* ) ) ) )
( define ( get-sessions )
( obtain-lock *session-table-lock* )
( let ( ( sessions ' ( ) ) )
2002-10-01 08:08:42 -04:00
( table-walk
2002-12-07 17:26:40 -05:00
( lambda ( session-id session-entry )
( set! sessions ( cons ( cons session-id session-entry ) sessions ) ) )
*session-table* )
( release-lock *session-table-lock* )
sessions ) )
( define ( get-continuations session-id )
( let ( ( session ( session-lookup session-id ) ) )
( if session
( let ( ( continuation-table-lock ( session-continuation-table-lock session ) )
( continuation-table ( session-continuation-table session ) )
2002-10-01 13:39:39 -04:00
( continuations ' ( ) ) )
( obtain-lock continuation-table-lock )
( table-walk
( lambda ( continuation-id continuation-entry )
( set! continuations ( cons ( cons continuation-id continuation-entry )
continuations ) ) )
continuation-table )
( release-lock continuation-table-lock )
continuations )
' ( ) ) ) )
2002-12-07 17:26:40 -05:00
( define ( delete-continuation! session-id continuation-id )
( let ( ( session ( session-lookup session-id ) ) )
( if session
( let ( ( continuation-table-lock ( session-continuation-table-lock session ) )
( continuation-table ( session-continuation-table session ) )
2002-10-01 13:39:39 -04:00
( continuations ' ( ) ) )
( obtain-lock continuation-table-lock )
( if ( table-ref continuation-table continuation-id )
( table-set! continuation-table continuation-id #f ) )
( release-lock continuation-table-lock ) ) ) ) )
2002-10-01 08:08:42 -04:00
2003-01-19 11:57:27 -05:00
( define ( set-surflet-data! new-data )
2002-12-07 17:26:40 -05:00
( let ( ( session ( session-lookup ( instance-session-id ) ) ) )
( if session
2002-10-02 20:15:44 -04:00
( begin
2003-01-19 11:57:27 -05:00
( set-session-surflet-data! session new-data )
2002-10-02 20:15:44 -04:00
#t )
#f ) ) )
2002-09-13 03:21:19 -04:00
2003-01-19 11:57:27 -05:00
( define ( get-surflet-data )
2002-12-07 17:26:40 -05:00
( let ( ( session ( session-lookup ( instance-session-id ) ) ) )
( if session
2003-01-19 11:57:27 -05:00
( session-surflet-data session )
2002-10-02 20:15:44 -04:00
( error "Instance no longer alive." ) ) ) )
2002-09-13 03:21:19 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ID generation
2002-09-21 16:18:49 -04:00
;; locking must be done by caller
;; FIXME?: this may loop forever, if the table is full (can this happen?)
( define ( generate-new-table-id table )
( let loop ( ( id ( random ) ) )
( if ( table-ref table id )
2002-09-13 03:21:19 -04:00
( loop ( random ) )
2002-09-21 16:18:49 -04:00
id ) ) )
2002-09-13 03:21:19 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-19 11:57:27 -05:00
;; SURFLETs CACHE
( define *surflet-table* ( make-string-table ) ) ; path-string is index
( define *surflet-table-lock* ( make-lock ) )
;; SURFLET-NAME is like "news-dir/latest-news.scm"
( define ( get-surflet-rt-structure surflet-name directory )
( let* ( ( full-surflet-name ( absolute-file-name surflet-name directory ) )
( load-surflet
2002-09-29 09:43:39 -04:00
( lambda ( cached? )
2002-11-05 05:21:15 -05:00
( with-fatal-handler*
2002-09-26 08:13:01 -04:00
( lambda ( condition decline )
2003-01-19 11:57:27 -05:00
( if cached? ( release-lock *surflet-table-lock* ) )
2002-09-26 08:13:01 -04:00
( decline ) )
( lambda ( )
;; load-config-file does not care about cwd(?)
;; --> absolute file name needed
2003-01-19 11:57:27 -05:00
( load-config-file full-surflet-name )
;; surflet-structure to load must be named "surflet"
( let ( ( surflet-structure ( reify-structure 'surflet ) ) )
( load-structure surflet-structure )
2002-09-29 09:43:39 -04:00
( if cached?
( begin
2003-01-19 11:57:27 -05:00
( table-set! *surflet-table* full-surflet-name
( cons surflet-structure
( file-last-mod full-surflet-name ) ) )
2002-09-29 09:43:39 -04:00
;; only now the lock may be released
2003-01-19 11:57:27 -05:00
( release-lock *surflet-table-lock* ) ) )
surflet-structure ) ) ) ) ) )
( if ( options-cache-surflets? )
2002-09-29 09:43:39 -04:00
( begin
2003-01-19 11:57:27 -05:00
;; The lock is only obtained and released, if surflets are
;; cached. LOAD-SURFLET gets the CACHED? parameter, so
2002-09-29 09:43:39 -04:00
;; nothing may happen, if in the meanwhile caching is turned
;; off.
2003-01-19 11:57:27 -05:00
( obtain-lock *surflet-table-lock* )
( let ( ( surflet ( table-ref *surflet-table* full-surflet-name ) ) )
( if surflet
( if ( equal? ( file-last-mod full-surflet-name )
( cdr surflet ) )
2002-09-29 09:43:39 -04:00
( begin
2003-01-19 11:57:27 -05:00
( release-lock *surflet-table-lock* )
( car surflet ) )
( load-surflet #t ) )
( load-surflet #t ) ) ) )
( load-surflet #f ) ) ) )
( define ( get-loaded-surflets )
( obtain-lock *surflet-table-lock* )
( let ( ( loaded-surflets ' ( ) ) )
2002-09-30 03:53:00 -04:00
( table-walk
2003-01-19 11:57:27 -05:00
( lambda ( surflet-path rt-structure )
( set! loaded-surflets ( cons surflet-path loaded-surflets ) ) )
*surflet-table* )
( release-lock *surflet-table-lock* )
loaded-surflets ) )
( define ( unload-surflet surflet-name )
( obtain-lock *surflet-table-lock* )
( if ( table-ref *surflet-table* surflet-name )
( table-set! *surflet-table* surflet-name #f ) )
( release-lock *surflet-table-lock* ) )
( define ( reset-surflet-cache! )
2002-09-13 03:21:19 -04:00
( with-fatal-error-handler*
( lambda ( condition decline )
2003-01-19 11:57:27 -05:00
( release-lock *surflet-table-lock* )
2002-09-13 03:21:19 -04:00
( decline ) )
( lambda ( )
2003-01-19 11:57:27 -05:00
( obtain-lock *surflet-table-lock* )
( set! *surflet-table* ( make-string-table ) )
( release-lock *surflet-table-lock* ) ) ) )
2002-09-13 03:21:19 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2002-12-07 17:26:40 -05:00
;; INSTANCE
( define *instance* ( make-thread-cell #f ) )
2002-09-13 03:21:19 -04:00
2002-12-07 17:26:40 -05:00
( define ( register-instance! session-id return-continuation )
( thread-cell-set! *instance*
( make-instance session-id return-continuation ) ) )
2002-09-13 03:21:19 -04:00
2002-12-07 17:26:40 -05:00
;(define (save-instance-return-continuation! return-continuation)
; (set-instance-session-id! (thread-cell-ref *instance*)
2002-09-13 03:21:19 -04:00
; return-continuation))
2002-12-07 17:26:40 -05:00
( define ( instance-session-id )
( really-instance-session-id ( thread-cell-ref *instance* ) ) )
2002-09-13 03:21:19 -04:00
2003-01-16 07:53:10 -05:00
;; unused
2002-12-07 17:26:40 -05:00
( define ( instance-return-continuation )
( really-instance-return-continuation ( thread-cell-ref *instance* ) ) )
2002-09-13 03:21:19 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; RESUME-URL
( define *resume-url-regexp* ( rx ( submatch ( * ( - printing ";" ) ) )
";k" ( submatch ( * digit ) ) ; Instance-ID
2002-09-19 07:16:29 -04:00
";c" ( + digit ) "-" ( submatch ( * digit ) ) ) ) ; Continuation-ID
2002-09-13 03:21:19 -04:00
2002-12-07 17:26:40 -05:00
( define ( make-resume-url path-string session-id continuation-counter continuation-id )
2002-09-26 08:13:01 -04:00
( string-append path-string
2002-12-07 17:26:40 -05:00
";k" ( number->string ( instance-session-id ) )
2002-09-19 07:16:29 -04:00
";c" ( number->string continuation-counter )
"-" ( number->string continuation-id ) ) )
2002-09-13 03:21:19 -04:00
2002-12-07 17:26:40 -05:00
( define ( resume-url-session-id id-url )
( receive ( session-id continuation-id )
2002-09-13 03:21:19 -04:00
( resume-url-ids id-url )
2002-12-07 17:26:40 -05:00
session-id ) )
2002-09-13 03:21:19 -04:00
( define ( resume-url-continuation-id id-url )
2002-12-07 17:26:40 -05:00
( receive ( session-id continuation-id )
2002-09-13 03:21:19 -04:00
( resume-url-ids id-url )
continuation-id ) )
( define ( resume-url-ids id-url )
( let ( ( match ( regexp-search *resume-url-regexp* id-url ) ) )
( if match
( values ( string->number ( match:substring match 2 ) )
( string->number ( match:substring match 3 ) ) )
2003-01-16 07:53:10 -05:00
( values #f #f ) ) ) )
2002-09-13 03:21:19 -04:00
2003-01-19 11:57:27 -05:00
( define ( resume-url-surflet-name id-url )
2002-09-13 03:21:19 -04:00
( let ( ( match ( regexp-search *resume-url-regexp* id-url ) ) )
( if match
( match:substring match 1 )
2003-01-16 07:53:10 -05:00
( values #f #f ) ) ) )
2002-09-13 03:21:19 -04:00
( define ( resume-url? id-url )
( regexp-search? *resume-url-regexp* id-url ) )
2002-09-21 16:18:49 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; thread-safe counter
( define-record-type counter :counter
( really-make-counter counter lock )
( counter counter-counter set-counter-counter! )
( lock counter-lock ) )
( define ( make-thread-safe-counter )
( really-make-counter 0 ( make-lock ) ) )
;;; read current value
( define ( thread-safe-counter counter )
( obtain-lock ( counter-lock counter ) )
( let ( ( result ( counter-counter counter ) ) )
( release-lock ( counter-lock counter ) )
result ) )
;;; make next value and return it
( define ( thread-safe-counter-next! counter )
( obtain-lock ( counter-lock counter ) )
( let ( ( result ( + 1 ( counter-counter counter ) ) ) )
( set-counter-counter! counter result )
( release-lock ( counter-lock counter ) )
result ) )
2002-11-05 05:21:15 -05:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Error-Handler
;;
;; Adopted from WITH-FATAL-ERROR-HANDLER, but handles everything that
;; is catchable. We must catch everything because we also want
2003-01-19 11:57:27 -05:00
;; exceptions (and warnings) to be catched (e.g. when the surflet is
2002-11-05 05:21:15 -05:00
;; loaded.)
( define ( with-fatal-handler* handler thunk )
( call-with-current-continuation
( lambda ( accept )
( ( call-with-current-continuation
( lambda ( k )
( with-handler ( lambda ( condition more )
( call-with-current-continuation
( lambda ( decline )
( k ( lambda ( ) ( handler condition decline ) ) ) ) )
( more ) ) ; Keep looking for a handler.
( lambda ( ) ( call-with-values thunk accept ) ) ) ) ) ) ) ) )
( define-syntax with-fatal-handler
( syntax-rules ( )
( ( with-fatal-handler handler body . . . )
( with-fatal-handler* handler
( lambda ( ) body . . . ) ) ) ) )
( define ( bad-gateway-error-response req path-string condition )
2003-01-10 04:52:35 -05:00
( make-error-response
2003-01-09 10:05:30 -05:00
( status-code bad-gateway ) req
2003-01-19 11:57:27 -05:00
( format #f "Error in surflet ~s." path-string )
2002-11-05 05:21:15 -05:00
condition ) )
2002-09-21 16:18:49 -04:00
2002-11-05 17:20:47 -05:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEBUGGING
2002-09-29 11:20:36 -04:00
( define ( debug fmt . args )
( if *debug*
( format #t "DEBUG: ~?~%" fmt args )
( force-output ) ) )