530 likes | 640 Views
Compiling Web Scripts for Apache. Jacob Matthews Luke Hoban Robby Findler Rice University. The Goal (Version 1). Write a CGI program like this: (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m))).
E N D
Compiling Web Scripts for Apache Jacob Matthews Luke Hoban Robby Findler Rice University
The Goal (Version 1) Write a CGI program like this: (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
(let ((n(read-from-web “Type a number: ”)) (m (read-from-web “And another: ”))) (display-to-web “The sum is: ” (+ n m)))
An Observation (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m)))
n = 4 An Observation (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m))) If we have the red and the blue box, we can resume the program at that point as many times as we want.
CPS Form There’s already a standard transformation that does what we want! CPS conversion, lambda-lifting, and closure conversion give us red boxes at every point and arrows connecting them (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m)))
n = 4 <INPUT TYPE=“hidden” NAME=“What’s Left?” VALUE=“A B C”> <INPUT TYPE=“hidden” NAME=“environment” VALUE=“n=4”> Read-from-web (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m)))
So what can we handle? • Creating, invoking, and passing closures • Creating and passing other basic values (cons, vector, string, etc) • Basic control constructs (if, let, cond, etc.) • call/cc
What can’t we handle? • variable assignment • mutable values • generative structures • exception handling • dynamic evaluation • input/output ports • threads • integration with native code • …
Plus … • … we have to be efficient! • … we have to be secure!
Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
sum = 9 Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
sum = 12 Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop)))) But then, the user hits the ‘Back’ button ...
sum = 9 Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop)))) sum = 9, not 12!
sum = 9 Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))
sum = [a] a = 9 Variable Assignment (let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))
a = 9 Variable Assignment (let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))
a = 12 Variable Assignment (let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))
a = 12 Variable Assignment (let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))
a = 12 Variable Assignment (let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))
sum = [a] a = 12 Variable Assignment (let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop)))) If the user hits the back button now, everything still works!
So where does the purple box go? • We need some place that’s associated with a particular user, but not a particular web page • Browser cookies might work
Mutable Values H do we handle other mutable values like cons cells, hash tables, and vectors?
Mutable Values (let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))
Mutable Values (let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop)))) lst = (cons #f ‘()) Same problem, different primitive
a = #f b = ‘() Mutable Values (let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop)))) lst = (cons [a][b])
Mutable Values But if we add to the purple box every time we make a list, we’ll have problems: • Even lists that never need to be saved get added • The purple box is never garbage-collected • There are too many constructors anyway!
Mutable Values • So instead, we get lazy! • Only add or update the purple box when we actually call read-from-web
Mutable Values (let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop)))) lst = (cons #f ‘())
a = #f b = ‘() Mutable Values (let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop)))) lst = (cons [a][b]) In fact, we add all new mutable values reachable from the environment
But Won’t the Store Still Be Too Big? • Yes! • Even worse: the store never shrinks! • Cookies aren’t feasible • For now, put (some of) the store on the server
Security As it stands, attackers can make up anything as the blue and purple information!
Security (if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page))
Security (if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page)) The attacker can’t choose the red boxes, but can choose where the arrows point …
Security (if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page)) … And that’s bad enough!
Security A solution: • Encrypt the contents of the hidden fields and the cookies • Keep a secret key only on the server
Efficiency • We’ve got too many red boxes! • They make the program larger • More arrows means larger values in the hidden fields and longer page download times
A Solution • “Full” CPS is too much - we don’t need all the red boxes!
Efficiency (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m))) The program never reaches (+ n m) without going directly on to display-to-web…
Efficiency (let ((n (read-from-web “Type a number: ”)) (m (read-from-web “and another: ”))) (display-to-web “The sum is: ” (+ n m))) … so we can combine the two boxes!