2003-01-25 11:09:03 -05:00
;; The SUrflet handler
2002-09-13 03:21:19 -04:00
;; Copyright Andreas Bernauer, 2002
2003-01-25 11:09:03 -05:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; GLOBALS
2002-09-13 03:21:19 -04:00
2003-01-25 11:09:03 -05:00
;;; DEBUGging
2002-09-30 03:53:00 -04:00
( define *debug* #t )
2002-09-29 11:20:36 -04:00
2003-01-25 11:09:03 -05:00
;;; OPTIONS for the SUrflet handler.
;; Preserved thread fluid because between different calls to
;; surflet-handler the options shall remain the same. SURFLET-HANDLER
;; sets the value (an option record, see end of file)
2002-10-04 11:51:04 -04:00
( define *options* ( make-preserved-thread-fluid #f ) )
2002-09-29 09:43:39 -04:00
2003-01-25 11:09:03 -05:00
;;; SURFLET-TABLE cache
( define *surflet-table* ( make-string-table ) ) ; path-string is index
( define *surflet-table-lock* ( make-lock ) )
2002-10-01 13:39:39 -04:00
2003-01-25 11:09:03 -05:00
;;; SESSION-TABLE
;; Every session gets an entry in the hash table. Entries are session
;; records.
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
2003-01-25 11:09:03 -05:00
;; INSTANCE is the session that is handled currently.
( define *instance* ( make-thread-cell #f ) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; SURFLET-HANDLER
2002-09-13 03:21:19 -04:00
2003-01-25 11:09:03 -05:00
;;; SURFLET-HANDLER
;; Loads a new or resumes a suspended SUrflet; returns a
;; (HTTP-)RESPONSE. SURFLET-PATH is a string pointing to the real
;; directory where the SUrflets are searched.
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-25 11:09:03 -05:00
;;; LAUNCH-NEW-SESSION
;; Loads and runs a new session of a SUrflet installing the RESET
;; boundary; returns a (HTTP-)RESPONSE. PATH-STRING is the virtual
;; path of the request, SURFLET-PATH is a string pointing to the real
;; directory of the SUrflets, and REQ the request of the browser.
2003-01-19 11:57:27 -05:00
( define ( launch-new-session path-string surflet-path req )
2003-01-25 11:09:03 -05:00
2002-10-02 19:47:07 -04:00
( cond
2003-01-25 11:09:03 -05:00
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 ) )
2003-01-25 11:09:03 -05:00
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.
2003-01-25 11:09:03 -05:00
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
2003-01-25 11:09:03 -05:00
;;; SESSION-SURVEILLANCE
;; Returns surveillance procedure to be fork-threaded, that kills a
;; session after TIME-TO-DIE (seconds) has expired. MEMO contains
;; current status of session.
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
2003-01-25 11:09:03 -05:00
;;; RESUME-URL
;; Resumes a suspended URL and returns a (HTTP-)RESPONSE. PATH-STRING
;; is the virtual path, SURFLET-PATH a string pointing to the real
;; directory of the SUrflets and REQ the request of the browser.
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 )
2003-01-25 11:09:03 -05:00
;; try to get continuation-table and then the continuation
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-25 11:09:03 -05:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; SURFLET-INTERFACE
;;; SEND/SUSPEND
;; Suspends current computation, saves current continuation, and
;; leaves current continuation via SHIFT with a (HTTP-)RESPONSE.
;; RESPONSE-MAKER is a procedure returnig a SURFLET-RESPONSE (that is,
;; eventually converted to a HTTP-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
2003-01-25 11:09:03 -05:00
;;; SEND/FINISH
;; Kills current session, and leaves current continuation returning
;; via SHIFT with a (HTTP-)RESPONSE. RESPONSE is a SURFLET-RESPONSE.
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
2003-01-25 11:09:03 -05:00
;;; SEND
;; Leaves current continuation via SHIFT with a
;; (HTTP-)RESPONSE. RESPONSE is a SURFLET-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-25 11:09:03 -05:00
;;; SEND-ERROR
;; Stops current computation, and leaves current continuation via
;; SHIFT with a (HTTP-)(ERROR-)RESPONSE. STATUS-CODE is a status code
;; from HTTP-RESPONSES, REQ a request (may be #f) and MESSAGES
;; contains further informations (arbitrary types).
2003-01-24 11:02:29 -05:00
( define ( send-error status-code req . messages )
( shift unused ( apply make-error-response
( cons status-code
( cons #f messages ) ) ) ) )
2003-01-25 11:09:03 -05:00
;;; MAKE-HTTP-RESPONSE
;; Converts a SURFLET-RESPONSE to a (HTTP-)RESPONSE. Returns a
;; (HTTP-)RESPONSE.
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-25 11:09:03 -05:00
;;; SESSIONS
;;; SESSION-LOOKUP
;; Looks up SESSION-ID in the *SESSION-TABLE* (locking) and returns
;; the SESSION record, if anby (#f otherwise).
2002-12-07 17:26:40 -05:00
( 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 ) )
2003-01-25 11:09:03 -05:00
;;; SESSION-NEXT-CONTINUATION-COUNTER
;; Increases the SESSION-CONTINUATION-COUNTER in the SESSION record by
;; one.
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
2003-01-25 11:09:03 -05:00
;;; DELETE-SESSION!
;; Deletes the session indicated by its number SESSION-ID from the
;; *SESSION-TABLE* (locking).
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
2003-01-25 11:09:03 -05:00
;;; SESSION-ADJUST-TIMEOUT!
;; Resets time-to-die of session indicated by its SESSION-ID number.
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
2003-01-25 11:09:03 -05:00
;;; ADJUST-TIMEOUT
;; Resets time-to-die of 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
2003-01-25 11:09:03 -05:00
;;; RESET-SESSION-TABLE!
;; Clears the *SESSION-TABLE* (locking)
2002-12-07 17:26:40 -05:00
( define ( reset-session-table! )
2003-01-25 11:09:03 -05:00
( with-fatal-handler
2002-09-29 11:20:36 -04:00
( 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 ) )
2003-01-25 11:09:03 -05:00
( obtain-lock *session-table-lock* )
;; notify session killing
( table-walk
( lambda ( session-id session )
( memo-killed! ( session-memo session ) ) )
*session-table* )
( set! *session-table* ( make-integer-table ) )
( release-lock *session-table* ) ) )
;;; GET-SESSIONS
;; Returns a list of all active sessions in *SESSION-TABLE*
;; (locking). The list elements are pairs of session-id and session
;; record.
2002-12-07 17:26:40 -05:00
( 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 ) )
2003-01-25 11:09:03 -05:00
;;; GET-CONTINUATIONS
;; Returns a list of all continuations of the session indicated by the
;; SESSION-ID number (locking). The list elements are pairs of
;; continuation id and continuation.
2002-12-07 17:26:40 -05:00
( 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 )
' ( ) ) ) )
2003-01-25 11:09:03 -05:00
;;; DELETE-CONTINUATION
;; Deletes continuation SESSION-ID, CONTINUATION-ID (locking).
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 ) ) ) ) )
2003-01-25 11:09:03 -05:00
;;; SET-SURFLET-DATA!, GET-SURFLET-DATA
;; Access to arbitrary data stored along with current session (no
;; locking!).
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-25 11:09:03 -05:00
;;; ID generation
;;; GENERATE-NEW-TABLE-ID
;; Returns a random integer not used in the hash TABLE (no
;; locking!). The locking has to happe elsewhere.
2002-09-21 16:18:49 -04:00
( define ( generate-new-table-id table )
( let loop ( ( id ( random ) ) )
( if ( table-ref table id )
2003-01-25 11:09:03 -05:00
;; FIXME?: this may loop forever, if the table is full (can
;; this ever happen?)
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
2003-01-25 11:09:03 -05:00
;;; GET-SURFLET-RT-STRUCTURE
;; Returns SUrflet's RT-STRUCTURE indicated by SURFLET-NAME (a virtual
;; path string) while managing the SUrflet cache *SURFLET-TABLE*
;; (locking).
2003-01-19 11:57:27 -05:00
( 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 ) ) ) )
2003-01-25 11:09:03 -05:00
;;; GET-LOADED-SURFLETS
;; Returns list of all loaded surflets (real path strings).
2003-01-19 11:57:27 -05:00
( 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 ) )
2003-01-25 11:09:03 -05:00
;;; UNLOAD-SURFLET
;; Removes SURFLET-NAME from the *SURFLET-TABLE* cache (locking).
2003-01-19 11:57:27 -05:00
( 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* ) )
2003-01-25 11:09:03 -05:00
;;; RESET-SURFLET-CACHE!
;; Clears *SURFLET-TABLE* (locking).
2003-01-19 11:57:27 -05:00
( define ( reset-surflet-cache! )
2003-01-25 11:09:03 -05:00
( with-fatal-handler
2002-09-13 03:21:19 -04:00
( 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 ) )
2003-01-25 11:09:03 -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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-25 11:09:03 -05:00
;;; INSTANCE
2002-09-13 03:21:19 -04:00
2003-01-25 11:09:03 -05:00
;;; REGISTER-INSTANCE!
;; Saves values for current session (in a record).
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
2003-01-25 11:09:03 -05:00
;;; INSTANCE-SESSION-ID
;; Returns session-id of current *INSTANCE*.
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-25 11:09:03 -05:00
;;; RESUME-URL
;; Functions and constants for generating and parsing Continuation
;; URLs (= resume URLs). Resume URLs look like
;; http://localhost:8088/surflet/admin-handler.scm;k757033335;c1-684902143?return54=
2002-09-13 03:21:19 -04:00
( define *resume-url-regexp* ( rx ( submatch ( * ( - printing ";" ) ) )
";k" ( submatch ( * digit ) ) ; Instance-ID
2003-01-25 11:09:03 -05:00
";c" ( + digit ) ; Continuation Counter
"-" ( submatch ( * digit ) ) ) ) ; Continuation-ID
2002-09-13 03:21:19 -04:00
2003-01-25 11:09:03 -05:00
;; All arguments are numbers except PATH-STRING, which is a string.
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
2003-01-25 11:09:03 -05:00
;; Return various parts of RESUME-URL
( define ( resume-url-session-id resume-url )
2002-12-07 17:26:40 -05:00
( receive ( session-id continuation-id )
2003-01-25 11:09:03 -05:00
( resume-url-ids resume-url )
2002-12-07 17:26:40 -05:00
session-id ) )
2002-09-13 03:21:19 -04:00
2003-01-25 11:09:03 -05:00
( define ( resume-url-continuation-id resume-url )
2002-12-07 17:26:40 -05:00
( receive ( session-id continuation-id )
2003-01-25 11:09:03 -05:00
( resume-url-ids resume-url )
2002-09-13 03:21:19 -04:00
continuation-id ) )
2003-01-25 11:09:03 -05:00
( define ( resume-url-ids resume-url )
( let ( ( match ( regexp-search *resume-url-regexp* resume-url ) ) )
2002-09-13 03:21:19 -04:00
( 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-25 11:09:03 -05:00
( define ( resume-url-surflet-name resume-url )
( let ( ( match ( regexp-search *resume-url-regexp* resume-url ) ) )
2002-09-13 03:21:19 -04:00
( 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
2003-01-25 11:09:03 -05:00
( define ( resume-url? resume-url )
( regexp-search? *resume-url-regexp* resume-url ) )
( define ( bad-gateway-error-response req path-string condition )
( make-error-response
( status-code bad-gateway ) req
( format #f "Error in surflet ~s." path-string )
condition ) )
2002-09-13 03:21:19 -04:00
2002-09-21 16:18:49 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2003-01-25 11:09:03 -05:00
;;; Record types
2002-09-21 16:18:49 -04:00
2003-01-25 11:09:03 -05:00
;;; SESSION: session-table entry for every new request on a surflet page
( define-record-type session :session
( make-session surflet-name memo
continuation-table continuation-table-lock
continuation-counter
surflet-data )
session?
( surflet-name session-surflet-name )
( memo session-memo set-session-memo! )
( continuation-table session-continuation-table )
( continuation-table-lock session-continuation-table-lock )
( continuation-counter session-continuation-counter )
( surflet-data session-surflet-data set-session-surflet-data! ) )
2002-09-21 16:18:49 -04:00
2003-01-25 11:09:03 -05:00
;;; MEMO: Information for session surveiller about session status
( 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 ) )
2002-09-21 16:18:49 -04:00
2003-01-25 11:09:03 -05:00
( define ( make-default-memo )
( make-memo 'kill #f #f ) )
2002-09-21 16:18:49 -04:00
2003-01-25 11:09:03 -05:00
;; caller must do locking stuff
( define ( memo-killed! memo )
( set-memo:message memo 'killed ) )
2002-09-21 16:18:49 -04:00
2002-11-05 05:21:15 -05:00
2003-01-25 11:09:03 -05:00
;;; INSTANCE: Every request corresponds to an instance.
( 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! ) )
;;; OPTIONS: options for the surflet-handler
( define-record-type options :options
2003-01-25 11:10:19 -05:00
( make-options surflet-path cache-surflets? session-lifetime )
2003-01-25 11:09:03 -05:00
options?
( surflet-path options:surflet-path set-options:surflet-path )
( cache-surflets? options:cache-surflets? set-options:cache-surflets? )
;; session lifetime is in seconds
( session-lifetime options:session-lifetime set-options:session-lifetime ) )
;; Constructor with defaults.
2003-01-25 11:10:19 -05:00
( define ( make-default-options surflet-path )
( make-options surflet-path #t 600 ) )
2002-09-21 16:18:49 -04:00
2003-01-25 11:09:03 -05:00
;; Selectors for *options* (preserved-thread-fluid)
( define ( make-fluid-selector selector )
( lambda ( ) ( selector ( thread-fluid *options* ) ) ) )
( define ( make-fluid-setter setter )
( lambda ( value )
( setter ( thread-fluid *options* ) value ) ) )
( define options-surflet-path ( make-fluid-selector options:surflet-path ) )
( define options-cache-surflets? ( make-fluid-selector options:cache-surflets? ) )
( define options-session-lifetime ( make-fluid-selector options:session-lifetime ) )
( define set-options-cache-surflets? ( make-fluid-setter set-options:cache-surflets? ) )
( define set-options-session-lifetime ( make-fluid-setter set-options:session-lifetime ) )
;;; SURFLET-RESPONSE: Surflets are expected to return this object type.
;;; 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 ) )
;; Allowed type for the data field.
( define ( valid-surflet-response-data? data )
( or ( string? data ) ( list? data ) ) )
;; For debug purposes
( define ( surflet-response->string surflet-response )
( format #f "#{Surflet-response Status: ~a Content-Type: ~s Headers: ~s~%~s~%"
( surflet-response-status surflet-response )
( surflet-response-content-type surflet-response )
( surflet-response-headers surflet-response )
( surflet-response-data surflet-response ) ) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; RANDOM SOURCE
( 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-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 ) ) )