116 lines
4.0 KiB
Scheme
116 lines
4.0 KiB
Scheme
;;; Path handler for uploading Scheme code to the SU web server -*- Scheme -*-
|
|
;;; This is really just an handler example demonstrating how to upload code
|
|
;;; into the server.
|
|
;;; Copyright (c) 1995 by Olin Shivers.
|
|
|
|
;;; Imports and non-R4RS'isms
|
|
;;; \r and \n in string for cr and lf.
|
|
;;; SWITCH conditional, ? for COND
|
|
;;; HTTP request record stucture
|
|
;;; HTTP-ERROR & reply codes
|
|
;;; Basic path handler support
|
|
;;; scsh syscalls
|
|
;;; Pretty-printing P proc.
|
|
;;; htmlout stuff
|
|
;;; SAFE-EVAL
|
|
;;; ERROR
|
|
;;; INDEX
|
|
;;; URI decoding
|
|
;;; string hacks (SKIP-WHITESPACE)
|
|
|
|
;;; HTML forms
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; This path handler is suitable for receiving code entered into an
|
|
;;; HTML text form. The Scheme code being uploaded is being POST'd to us
|
|
;;; (from a form). See http-forms.scm for info on the format of this kind
|
|
;;; of request. After parsing the request into the submitted string, we
|
|
;;; parse *that* into a Scheme sexp with READ, and eval it.
|
|
|
|
;;; (do/timeout secs thunk)
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; Run THUNK, and gun it down if it hasn't finished in SECS seconds.
|
|
;;; Returns nothing useful, and THUNK gets executed in a subprocess,
|
|
;;; so its side-effects are invisible, as well. This is a clever kludge --
|
|
;;; it uses three subprocesses -- but I don't have interrupts, so I'm hosed.
|
|
|
|
(define (do/timeout* secs thunk)
|
|
(run (begin (let ((timer (fork (lambda () (sleep secs))))
|
|
(worker (fork thunk)))
|
|
(receive (process status) (wait-any)
|
|
(ignore-errors
|
|
(lambda ()
|
|
(signal-process (proc:pid (if (eq? worker process)
|
|
timer
|
|
worker))
|
|
signal/kill))))))))
|
|
(define-syntax do/timeout
|
|
(syntax-rules ()
|
|
((do/timeout secs body ...) (do/timeout* secs (lambda () body ...)))))
|
|
|
|
|
|
;;; The path handler for seval ops.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define (seval-handler path req)
|
|
(let ((request-method (request:method req)))
|
|
(cond
|
|
((string=? request-method "POST") ; Could do others also.
|
|
|
|
(let ((modern-protocol? (not (v0.9-request? req))))
|
|
(if modern-protocol?
|
|
(begin
|
|
(begin-http-header #t 200)
|
|
(write-string "Content-type: text/html\r\n\r\n")))
|
|
(with-tag #t HEAD ()
|
|
(newline)
|
|
(emit-title #t "Scheme program output"))
|
|
(newline))
|
|
|
|
(with-tag #t BODY ()
|
|
(newline)
|
|
(let ((sexp (read-request-sexp req)))
|
|
(do/timeout
|
|
10
|
|
(receive vals
|
|
;; Do the computation.
|
|
(begin (emit-header #t 2 "Output from execution")
|
|
(newline)
|
|
(with-tag #t PRE ()
|
|
(newline)
|
|
(force-output) ; In case we're gunned down.
|
|
(eval-safely sexp)))
|
|
|
|
;; Pretty-print the returned value(s).
|
|
(emit-header #t 2 "Return value(s)")
|
|
(with-tag #t PRE ()
|
|
(for-each p vals)))))))
|
|
|
|
(else (http-error http-reply/method-not-allowed #f req)))))
|
|
|
|
|
|
;;; Read an HTTP request entity body from stdin. The Content-length:
|
|
;;; element of request REQ's header tells how many bytes to this entity
|
|
;;; is. The entity should be a URI-encoded form body. Pull out the
|
|
;;; program=<stuff>
|
|
;;; string, extract <stuff>, uri-decode it, parse that into an s-expression,
|
|
;;; and return it.
|
|
|
|
(define (read-request-sexp req)
|
|
(cond
|
|
((get-header (request:headers req) 'content-length) =>
|
|
(lambda (cl-str) ; Take the first Content-length: header,
|
|
(let* ((cl-start (skip-whitespace cl-str)) ; skip whitespace,
|
|
(cl (if cl-start ; & convert to
|
|
(string->number (substring cl-str ; a number.
|
|
cl-start
|
|
(string-length cl-str)))
|
|
0)) ; All whitespace?? -- WTF.
|
|
(qs (read-string cl)) ; Read in CL chars,
|
|
(q (parse-html-form-query qs)) ; and parse them up.
|
|
(s (cond ((assoc "program" q) => cdr)
|
|
(else (error "No program in entity body.")))))
|
|
(http-log "Seval sexp:~%~s~%" s)
|
|
(read (make-string-input-port s)))))
|
|
(else (http-error http-reply/bad-request req
|
|
"No Content-length: field in POST request."))))
|