diff --git a/collects/scribblings/more/more.scrbl b/collects/scribblings/more/more.scrbl index 0cb6539140..720d286bd7 100644 --- a/collects/scribblings/more/more.scrbl +++ b/collects/scribblings/more/more.scrbl @@ -2,13 +2,13 @@ @(require scribble/manual scribble/urls scribble/eval - "../quick/keep.ss" + "../quick/keep.rkt" (for-label scheme - scheme/enter + racket/enter readline net/url xml - scheme/control)) + racket/control)) @(define quick @other-manual['(lib "quick.scrbl" "scribblings/quick")]) @(define guide @other-manual['(lib "guide.scrbl" "scribblings/guide")]) @@ -16,7 +16,7 @@ @(define more-eval (make-base-eval)) @(interaction-eval #:eval more-eval (define (show-load re?) - (fprintf (current-error-port) " [~aloading serve.ss]\n" (if re? "re-" "")))) + (fprintf (current-error-port) " [~aloading serve.rkt]\n" (if re? "re-" "")))) @(interaction-eval #:eval more-eval (define (serve n) void)) @(interaction-eval #:eval more-eval @@ -39,13 +39,13 @@ " in plain text: " (link file "step " which) "."))) -@title{@bold{More}: Systems Programming with PLT Scheme} +@title{@bold{More}: Systems Programming with Racket} @author["Matthew Flatt"] -In contrast to the impression that @|quick| may give, PLT Scheme is +In contrast to the impression that @|quick| may give, Racket is not just another pretty face. Underneath the graphical facade of -DrScheme lies a sophisticated toolbox for managing threads and +DrRacket lies a sophisticated toolbox for managing threads and processes, which is the subject of this tutorial. Specifically, we show how to build a secure, multi-threaded, @@ -54,39 +54,39 @@ the language than in @|quick|, and we expect you to click on syntax or function names that you don't recognize (which will take you to the relevant documentation). Beware that the last couple of sections present material that is normally considered difficult. If you're -still new to Scheme and have relatively little programming experience, +still new to Racket and have relatively little programming experience, you may want to skip to @|guide|. To get into the spirit of this tutorial, we suggest that you set -DrScheme aside for a moment, and switch to raw @exec{mzscheme} in a +DrRacket aside for a moment, and switch to raw @exec{racket} in a terminal. You'll also need a text editor, such as @exec{emacs} or @exec{vi}. Finally, you'll need a web client, perhaps @exec{lynx} or @exec{firefox}. @margin-note{Of course, if you're already spoiled, you can keep using -DrScheme.} +DrRacket.} @; ---------------------------------------------------------------------- @section{Ready...} -@link[url:download-drscheme]{Download PLT Scheme}, install, and then -start @exec{mzscheme} with no command-line arguments: +@link[url:download-drracket]{Download Racket}, install, and then +start @exec{racket} with no command-line arguments: @verbatim[#:indent 2]{ - $ mzscheme - Welcome to MzScheme + $ racket + Welcome to Racket > } If you're using a plain terminal, if you have GNU Readline installed -on your system, and if you'd like Readline support in @exec{mzscheme}, -then evaluate @scheme[(require readline)]. If you also evaluate -@scheme[(install-readline!)], then your @filepath{~/.mzschemerc} is -updated to load Readline whenever you start @exec{mzscheme} for +on your system, and if you'd like Readline support in @exec{racket}, +then evaluate @racket[(require readline)]. If you also evaluate +@racket[(install-readline!)], then your @filepath{~/.racketrc} is +updated to load Readline whenever you start @exec{racket} for interactive evaluation. @margin-note{Unfortunately, for legal reasons related to GPL vs. LGPL, - @exec{mzscheme} cannot provide Readline automatically.} + @exec{racket} cannot provide Readline automatically.} @interaction[ (eval:alts (require readline) (void)) @@ -96,11 +96,11 @@ interactive evaluation. @; ---------------------------------------------------------------------- @section{Set...} -In the same directory where you started @exec{mzscheme}, create a text -file @filepath{serve.ss}, and start it like this: +In the same directory where you started @exec{racket}, create a text +file @filepath{serve.rkt}, and start it like this: -@schememod[ -scheme +@racketmod[ +racket (define (go) 'yep-it-works) @@ -111,35 +111,35 @@ scheme @; ---------------------------------------------------------------------- @section{Go!} -Back in @exec{mzscheme}, try loading the file and running @scheme[go]: +Back in @exec{racket}, try loading the file and running @racket[go]: @interaction[ #:eval more-eval -(eval:alts (enter! "serve.ss") (show-load #f)) +(eval:alts (enter! "serve.rkt") (show-load #f)) (eval:alts (go) 'yep-it-works) ] -Try modifying @filepath{serve.ss}, and then run @scheme[(enter! -"serve.ss")] again to re-load the module, and then check your changes. +Try modifying @filepath{serve.rkt}, and then run @racket[(enter! +"serve.rkt")] again to re-load the module, and then check your changes. @; ---------------------------------------------------------------------- @section{``Hello World'' Server} -We'll implement the web server through a @scheme[serve] function that +We'll implement the web server through a @racket[serve] function that takes a IP port number for client connections: -@schemeblock[ +@racketblock[ (define (serve port-no) ...) ] The server accepts TCP connections through a @defterm{listener}, which -we create with @scheme[tcp-listen]. To make interactive development -easier, we supply @scheme[#t] as the third argument to -@scheme[tcp-listen], which lets us re-use the port number without +we create with @racket[tcp-listen]. To make interactive development +easier, we supply @racket[#t] as the third argument to +@racket[tcp-listen], which lets us re-use the port number without waiting on TCP timeouts. -@schemeblock[ +@racketblock[ (define (serve port-no) (define listener (tcp-listen port-no 5 #t)) ...) @@ -147,7 +147,7 @@ waiting on TCP timeouts. The server must loop to accept connections from the listener: -@schemeblock[ +@racketblock[ (define (serve port-no) (define listener (tcp-listen port-no 5 #t)) (define (loop) @@ -156,11 +156,11 @@ The server must loop to accept connections from the listener: (loop)) ] -Our @scheme[accept-and-handle] function accepts a connection using -@scheme[tcp-accept], which returns two values: a stream for input from +Our @racket[accept-and-handle] function accepts a connection using +@racket[tcp-accept], which returns two values: a stream for input from the client, and a stream for output to the client. -@schemeblock[ +@racketblock[ (define (accept-and-handle listener) (define-values (in out) (tcp-accept listener)) (handle in out) @@ -171,7 +171,7 @@ the client, and a stream for output to the client. To handle a connection, for now, we'll read and discard the request header, and then write a ``Hello, world!'' web page as the result: -@schemeblock[ +@racketblock[ (define (handle in out) (code:comment @#,t{Discard the request header (up to blank line):}) (regexp-match #rx"(\r\n|^)\r\n" in) @@ -181,23 +181,23 @@ header, and then write a ``Hello, world!'' web page as the result: (display "Hello, world!" out)) ] -Note that @scheme[regexp-match] operates directly on the input stream, +Note that @racket[regexp-match] operates directly on the input stream, which is easier than bothering with individual lines. @whole-prog["1"] -Copy the above three definitions---@scheme[serve], -@scheme[accept-and-handle], and @scheme[handle]---into -@filepath{serve.ss} and re-load: +Copy the above three definitions---@racket[serve], +@racket[accept-and-handle], and @racket[handle]---into +@filepath{serve.rkt} and re-load: @interaction[ #:eval more-eval -(eval:alts (enter! "serve.ss") (show-load #t)) +(eval:alts (enter! "serve.rkt") (show-load #t)) (eval:alts (serve 8080) (void)) ] Now point your browser to @tt{http://localhost:8080} (assuming that -you used @scheme[8080] as the port number, and that the browser is +you used @racket[8080] as the port number, and that the browser is running on the same machine) to receive a friendly greeting from your web server. @@ -205,10 +205,10 @@ web server. @section{Server Thread} Before we can make the web server respond in more interesting ways, we -need to get a Scheme prompt back. Typing Ctl-C in your terminal window +need to get a Racket prompt back. Typing Ctl-C in your terminal window interrupts the server loop: -@margin-note{In DrScheme, instead of typing Ctl-C, click the +@margin-note{In DrRacket, instead of typing Ctl-C, click the @onscreen{Stop} button once.} @interaction[ @@ -225,15 +225,15 @@ number: (eval:alts (serve 8080) (show-fail 8080)) ] -The problem is that the listener that we created with @scheme[serve] +The problem is that the listener that we created with @racket[serve] is still listening on the original port number. To avoid this problem, let's put the listener loop in its own thread, -and have @scheme[serve] return immediately. Furthermore, we'll have -@scheme[serve] return a function that can be used to shut down the +and have @racket[serve] return immediately. Furthermore, we'll have +@racket[serve] return a function that can be used to shut down the server thread and TCP listener: -@schemeblock[ +@racketblock[ (define (serve port-no) (define listener (tcp-listen port-no 5 #t)) (define (loop) @@ -251,7 +251,7 @@ Try the new one: @interaction[ #:eval more-eval -(eval:alts (enter! "serve.ss") (show-load #t)) +(eval:alts (enter! "serve.rkt") (show-load #t)) (define stop (serve 8081)) ] @@ -274,7 +274,7 @@ as you like: In the same way that we put the main server loop into a background thread, we can put each individual connection into its own thread: -@schemeblock[ +@racketblock[ (define (accept-and-handle listener) (define-values (in out) (tcp-accept listener)) (thread @@ -288,8 +288,8 @@ thread, we can put each individual connection into its own thread: With this change, our server can now handle multiple threads at once. The handler is so fast that this fact will be difficult to -detect, however, so try inserting @scheme[(sleep (random 10))] before -the @scheme[handle] call above. If you make multiple connections from +detect, however, so try inserting @racket[(sleep (random 10))] before +the @racket[handle] call above. If you make multiple connections from the web browser at roughly the same time, some will return soon, and some will take up to 10 seconds. The random delays will be independent of the order in which you started the connections. @@ -304,10 +304,10 @@ like to implement a timeout for each connection thread. One way to implement the timeout is to create a second thread that waits for 10 seconds, and then kills the thread that calls -@scheme[handle]. Threads are lightweight enough in Scheme that this +@racket[handle]. Threads are lightweight enough in Racket that this watcher-thread strategy works well: -@schemeblock[ +@racketblock[ (define (accept-and-handle listener) (define-values (in out) (tcp-accept listener)) (define t (thread @@ -322,22 +322,25 @@ watcher-thread strategy works well: ] This first attempt isn't quite right, because when the thread is -killed, its @scheme[in] and @scheme[out] streams remain open. We +killed, its @racket[in] and @racket[out] streams remain open. We could add code to the watcher thread to close the streams as well as -kill the thread, but Scheme offers a more general shutdown mechanism: +kill the thread, but Racket offers a more general shutdown mechanism: @defterm{custodians}. A custodian is a kind of container for all resources other than memory, and it supports a -@scheme[custodian-shutdown-all] operation that terminates and closes +@racket[custodian-shutdown-all] operation that terminates and closes all resources within the container, whether they're threads, streams, or other kinds of limited resources. Whenever a thread or stream is created, it is placed into the current -custodian as determined by the @scheme[current-custodian] +custodian as determined by the @racket[current-custodian] parameter. To place everything about a connection into a custodian, we -@scheme[parameterize] all the resource creations to go into a new +@racket[parameterize] all the resource creations to go into a new custodian: -@schemeblock[ +@margin-note{See @secref[#:doc '(lib "scribblings/guide/guide.scrbl") "parameterize"] + for an introduction to parameters.} + +@racketblock[ (define (accept-and-handle listener) (define cust (make-custodian)) (parameterize ([current-custodian cust]) @@ -352,16 +355,16 @@ custodian: (custodian-shutdown-all cust)))) ] -With this implementation, @scheme[in], @scheme[out], and the thread -that calls @scheme[handle] all belong to @scheme[cust]. In addition, -if we later change @scheme[handle] so that it, say, opens a file, then -the file handles will also belong to @scheme[cust], so they will be -reliably closed when @scheme[cust] is shut down. +With this implementation, @racket[in], @racket[out], and the thread +that calls @racket[handle] all belong to @racket[cust]. In addition, +if we later change @racket[handle] so that it, say, opens a file, then +the file handles will also belong to @racket[cust], so they will be +reliably closed when @racket[cust] is shut down. -In fact, it's a good idea to change @scheme[serve] so that it uses a +In fact, it's a good idea to change @racket[serve] so that it uses a custodian, too: -@schemeblock[ +@racketblock[ (define (serve port-no) (define main-cust (make-custodian)) (parameterize ([current-custodian main-cust]) @@ -374,7 +377,7 @@ custodian, too: (custodian-shutdown-all main-cust))) ] -That way, the @scheme[main-cust] created in @scheme[serve] not only +That way, the @racket[main-cust] created in @racket[serve] not only owns the TCP listener and the main server thread, it also owns every custodian created for a connection. Consequently, the revised shutdown procedure for the server immediately terminates all active connections, @@ -382,17 +385,17 @@ in addition to the main server loop. @whole-prog["4"] -After updating the @scheme[serve] and @scheme[accept-and-handle] +After updating the @racket[serve] and @racket[accept-and-handle] functions as above, here's how you can simulate a malicious client: @interaction[ #:eval more-eval -(eval:alts (enter! "serve.ss") (show-load #t)) +(eval:alts (enter! "serve.rkt") (show-load #t)) (define stop (serve 8081)) (eval:alts (define-values (cin cout) (tcp-connect "localhost" 8081)) (void)) ] -Now wait 10 seconds. If you try reading from @scheme[cin], which is +Now wait 10 seconds. If you try reading from @racket[cin], which is the stream that sends data from the server back to the client, you'll find that the server has shut down the connection: @@ -422,12 +425,12 @@ URLs. To parse the incoming URL and to more easily format HTML output, we'll require two extra libraries: -@schemeblock[ +@racketblock[ (require xml net/url) ] -The @schememodname[xml] library gives us @scheme[xexpr->string], which -takes a Scheme value that looks like HTML and turns it into actual +The @racketmodname[xml] library gives us @racket[xexpr->string], which +takes a Racket value that looks like HTML and turns it into actual HTML: @interaction[ @@ -435,11 +438,11 @@ HTML: (xexpr->string '(html (head (title "Hello")) (body "Hi!"))) ] -We'll assume that our new @scheme[dispatch] function (to be written) +We'll assume that our new @racket[dispatch] function (to be written) takes a requested URL and produces a result value suitable to use with -@scheme[xexpr->string] to send back to the client: +@racket[xexpr->string] to send back to the client: -@schemeblock[ +@racketblock[ (define (handle in out) (define req (code:comment @#,t{Match the first line to extract the request:}) @@ -456,8 +459,8 @@ takes a requested URL and produces a result value suitable to use with (display (xexpr->string xexpr) out)))) ] -The @schememodname[net/url] library gives us @scheme[string->url], -@scheme[url-path], @scheme[path/param-path], and @scheme[url-query] +The @racketmodname[net/url] library gives us @racket[string->url], +@racket[url-path], @racket[path/param-path], and @racket[url-query] for getting from a string to parts of the URL that it represents: @interaction[ @@ -468,11 +471,11 @@ for getting from a string to parts of the URL that it represents: (url-query u) ] -We use these pieces to implement @scheme[dispatch]. The -@scheme[dispatch] function consults a hash table that maps an initial -path element, like @scheme["foo"], to a handler function: +We use these pieces to implement @racket[dispatch]. The +@racket[dispatch] function consults a hash table that maps an initial +path element, like @racket["foo"], to a handler function: -@schemeblock[ +@racketblock[ (define (dispatch str-path) (code:comment @#,t{Parse the request as a URL:}) (define url (string->url str-path)) @@ -493,17 +496,17 @@ path element, like @scheme["foo"], to a handler function: (define dispatch-table (make-hash)) ] -With the new @scheme[require] import and new @scheme[handle], -@scheme[dispatch], and @scheme[dispatch-table] definitions, our +With the new @racket[require] import and new @racket[handle], +@racket[dispatch], and @racket[dispatch-table] definitions, our ``Hello World!'' server has turned into an error server. You don't have -to stop the server to try it out. After modifying @filepath{serve.ss} -with the new pieces, evaluate @scheme[(enter! "serve.ss")] and then +to stop the server to try it out. After modifying @filepath{serve.rkt} +with the new pieces, evaluate @racket[(enter! "serve.rkt")] and then try again to connect to the server. The web browser should show an ``Unknown page'' error in red. -We can register a handler for the @scheme["hello"] path like this: +We can register a handler for the @racket["hello"] path like this: -@schemeblock[ +@racketblock[ (hash-set! dispatch-table "hello" (lambda (query) `(html (body "Hello, World!")))) @@ -511,25 +514,25 @@ We can register a handler for the @scheme["hello"] path like this: @whole-prog["5"] -After adding these lines and evaluating @scheme[(enter! "serve.ss")], +After adding these lines and evaluating @racket[(enter! "serve.rkt")], opening @tt{http://localhost:8081/hello} should produce the old greeting. @; ---------------------------------------------------------------------- @section{Servlets and Sessions} -Using the @scheme[query] argument that is passed to a handler by -@scheme[dispatch], a handler can respond to values that a user +Using the @racket[query] argument that is passed to a handler by +@racket[dispatch], a handler can respond to values that a user supplies through a form. The following helper function constructs an HTML form. The -@scheme[label] argument is a string to show the user. The -@scheme[next-url] argument is a destination for the form results. The -@scheme[hidden] argument is a value to propagate through the form as a -hidden field. When the user responds, the @scheme["number"] field in +@racket[label] argument is a string to show the user. The +@racket[next-url] argument is a destination for the form results. The +@racket[hidden] argument is a value to propagate through the form as a +hidden field. When the user responds, the @racket["number"] field in the form holds the user's value: -@schemeblock[ +@racketblock[ (define (build-request-page label next-url hidden) `(html (head (title "Enter a Number to Add")) @@ -547,7 +550,10 @@ the form holds the user's value: Using this helper function, we can create a servlet that generates as many ``hello''s as a user wants: -@schemeblock[ +@margin-note{See @secref[#:doc '(lib "scribblings/guide/guide.scrbl") "for"] + for an introduction to forms like @racket[for/list].} + +@racketblock[ (define (many query) (build-request-page "Number of greetings:" "/reply" "")) @@ -563,23 +569,23 @@ many ``hello''s as a user wants: @whole-prog["6"] As usual, once you have added these to your program, update with -@scheme[(enter! "serve.ss")], and then visit +@racket[(enter! "serve.rkt")], and then visit @tt{http://localhost:8081/many}. Provide a number, and you'll receive a new page with that many ``hello''s. @; ---------------------------------------------------------------------- @section{Limiting Memory Use} -With our latest @scheme["many"] servlet, we seem to have a new +With our latest @racket["many"] servlet, we seem to have a new problem: a malicious client could request so many ``hello''s that the server runs out of memory. Actually, a malicious client could also supply an HTTP request whose first line is arbitrarily long. The solution to this class of problems is to limit the memory use of a -connection. Inside @scheme[accept-and-handle], after the definition of -@scheme[cust], add the line +connection. Inside @racket[accept-and-handle], after the definition of +@racket[cust], add the line -@schemeblock[(custodian-limit-memory cust (* 50 1024 1024))] +@racketblock[(custodian-limit-memory cust (* 50 1024 1024))] @whole-prog["7"] @@ -591,15 +597,15 @@ for each other's memory use, so one misbehaving connection will not interfere with a different one. So, with the new line above, and assuming that you have a couple of -hundred megabytes available for the @exec{mzscheme} process to use, +hundred megabytes available for the @exec{racket} process to use, you shouldn't be able to crash the web server by requesting a ridiculously large number of ``hello''s. -Given the @scheme["many"] example, it's a small step to a web server -that accepts arbitrary Scheme code to execute on the server. In that +Given the @racket["many"] example, it's a small step to a web server +that accepts arbitrary Racket code to execute on the server. In that case, there are many additional security issues besides limiting processor time and memory consumption. The -@schememodname[scheme/sandbox] library provides support to managing +@racketmodname[racket/sandbox] library provides support to managing all those other issues. @; ---------------------------------------------------------------------- @@ -607,9 +613,9 @@ all those other issues. As a systems example, the problem of implementing a web server exposes many system and security issues where a programming language can -help. The web-server example also leads to a classic, advanced Scheme +help. The web-server example also leads to a classic, advanced Racket topic: @defterm{continuations}. In fact, this facet of a web server -needs @defterm{delimited continuations}, which PLT Scheme provides. +needs @defterm{delimited continuations}, which Racket provides. The problem solved by continuations is related to servlet sessions and user input, where a computation spans multiple client connections @@ -619,11 +625,11 @@ a mixture of techniques (e.g., to take advantage of the browser's ``back'' button). As the multi-connection computation becomes more complex, propagating -arguments through @scheme[query] becomes increasingly tedious. For +arguments through @racket[query] becomes increasingly tedious. For example, we can implement a servlet that takes two numbers to add by using the hidden field in the form to remember the first number: -@schemeblock[ +@racketblock[ (define (sum query) (build-request-page "First number:" "/one" "")) @@ -647,7 +653,7 @@ using the hidden field in the form to remember the first number: While the above works, we would much rather write such computations in a direct style: -@schemeblock[ +@racketblock[ (define (sum2 query) (define m (get-number "First number:")) (define n (get-number "Second number:")) @@ -656,32 +662,32 @@ a direct style: (hash-set! dispatch-table "sum2" sum2) ] -The problem is that @scheme[get-number] needs to send an HTML response +The problem is that @racket[get-number] needs to send an HTML response back for the current connection, and then it must obtain a response through a new connection. That is, somehow it needs to convert the -page generated by @scheme[build-request-page] into a @scheme[query] +page generated by @racket[build-request-page] into a @racket[query] result: -@schemeblock[ +@racketblock[ (define (get-number label) (define query ... (build-request-page label ...) ...) (number->string (cdr (assq 'number query)))) ] -Continuations let us implement a @scheme[send/suspend] operation that -performs exactly that operation. The @scheme[send/suspend] procedure +Continuations let us implement a @racket[send/suspend] operation that +performs exactly that operation. The @racket[send/suspend] procedure generates a URL that represents the current connection's computation, capturing it as a continuation. It passes the generated URL to a procedure that creates the query page; this query page is used as the result of the current connection, and the surrounding computation -(i.e., the continuation) is aborted. Finally, @scheme[send/suspend] +(i.e., the continuation) is aborted. Finally, @racket[send/suspend] arranges for a request to the generated URL (in a new connection) to restore the aborted computation. -Thus, @scheme[get-number] is implemented as follows: +Thus, @racket[get-number] is implemented as follows: -@schemeblock[ +@racketblock[ (define (get-number label) (define query (code:comment @#,t{Generate a URL for the current computation:}) @@ -695,40 +701,39 @@ Thus, @scheme[get-number] is implemented as follows: (string->number (cdr (assq 'number query)))) ] -We still have to implement @scheme[send/suspend]. Plain Scheme's -@scheme[call/cc] is not quite enough, so we import a library of -control operators: +We still have to implement @racket[send/suspend]. For that task, we +import a library of control operators: -@schemeblock[(require scheme/control)] +@racketblock[(require racket/control)] -Specifically, we need @scheme[prompt] and @scheme[abort] from -@schememodname[scheme/control]. We use @scheme[prompt] to mark the +Specifically, we need @racket[prompt] and @racket[abort] from +@racketmodname[racket/control]. We use @racket[prompt] to mark the place where a servlet is started, so that we can abort a computation -to that point. Change @scheme[handle] by wrapping an @scheme[prompt] -around the call to @scheme[dispatch]: +to that point. Change @racket[handle] by wrapping an @racket[prompt] +around the call to @racket[dispatch]: -@schemeblock[ +@racketblock[ (define (handle in out) .... (let ([xexpr (prompt (dispatch (list-ref req 1)))]) ....)) ] -Now, we can implement @scheme[send/suspend]. We use @scheme[call/cc] -in the guise of @scheme[let/cc], which captures the current -computation up to an enclosing @scheme[prompt] and binds that -computation to an identifier---@scheme[k], in this case: +Now, we can implement @racket[send/suspend]. We use @racket[call/cc] +in the guise of @racket[let/cc], which captures the current +computation up to an enclosing @racket[prompt] and binds that +computation to an identifier---@racket[k], in this case: -@schemeblock[ +@racketblock[ (define (send/suspend mk-page) (let/cc k ...)) ] Next, we generate a new dispatch tag, and we record the mapping from -the tag to @scheme[k]: +the tag to @racket[k]: -@schemeblock[ +@racketblock[ (define (send/suspend mk-page) (let/cc k (define tag (format "k~a" (current-inexact-milliseconds))) @@ -737,10 +742,10 @@ the tag to @scheme[k]: ] Finally, we abort the current computation, supplying instead the page -that is built by applying the given @scheme[mk-page] to a URL for the +that is built by applying the given @racket[mk-page] to a URL for the generated tag: -@schemeblock[ +@racketblock[ (define (send/suspend mk-page) (let/cc k (define tag (format "k~a" (current-inexact-milliseconds))) @@ -751,21 +756,21 @@ generated tag: When the user submits the form, the handler associated with the form's URL is the old computation, stored as a continuation in the dispatch table. Calling the continuation (like a function) restores the old -computation, passing the @scheme[query] argument back to that +computation, passing the @racket[query] argument back to that computation. @whole-prog["9" #t] -In summary, the new pieces are: @scheme[(require scheme/control)], -adding @scheme[prompt] inside @scheme[handle], the definitions of -@scheme[send/suspend], @scheme[get-number], and @scheme[sum2], and -@scheme[(hash-set! dispatch-table "sum2" sum2)]. Once you have +In summary, the new pieces are: @racket[(require racket/control)], +adding @racket[prompt] inside @racket[handle], the definitions of +@racket[send/suspend], @racket[get-number], and @racket[sum2], and +@racket[(hash-set! dispatch-table "sum2" sum2)]. Once you have the server updated, visit @tt{http://localhost:8081/sum2}. @; ---------------------------------------------------------------------- @section{Where to Go From Here} -The PLT Scheme distribution includes a production-quality web server +The Racket distribution includes a production-quality web server that addresses all of the design points mentioned here and more. To learn more, see the tutorial @other-manual['(lib "web-server/scribblings/tutorial/continue.scrbl")], the @@ -773,8 +778,8 @@ documentation @other-manual['(lib "web-server/scribblings/web-server.scrbl")], or the research paper @cite["Krishnamurthi07"]. -Otherwise, if you arrived here as part of an introduction to PLT -Scheme, then your next stop is probably @|guide|. +Otherwise, if you arrived here as part of an introduction to +Racket, then your next stop is probably @|guide|. If the topics covered here are the kind that interest you, see also @secref["concurrency" #:doc '(lib @@ -783,10 +788,10 @@ If the topics covered here are the kind that interest you, see also "scribblings/reference/reference.scrbl")]. Some of this material is based on relatively recent research, and more -information can be found in papers written by the authors of PLT -Scheme, including papers on MrEd @cite["Flatt99"], memory accounting -@cite["Wick04"], kill-safe abstractions @cite["Flatt04"], and -delimited continuations @cite["Flatt07"]. +information can be found in papers written by the authors of Racket, +including papers on GRacket (formerly ``MrEd'') @cite["Flatt99"], +memory accounting @cite["Wick04"], kill-safe abstractions +@cite["Flatt04"], and delimited continuations @cite["Flatt07"]. @; ---------------------------------------------------------------------- diff --git a/collects/scribblings/more/step0.txt b/collects/scribblings/more/step0.txt index 525b7202e2..a6db727a2e 100644 --- a/collects/scribblings/more/step0.txt +++ b/collects/scribblings/more/step0.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket (define (go) 'yep-it-works) diff --git a/collects/scribblings/more/step1.txt b/collects/scribblings/more/step1.txt index 27820cc327..5a7c49544d 100644 --- a/collects/scribblings/more/step1.txt +++ b/collects/scribblings/more/step1.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket (define (serve port-no) (define listener (tcp-listen port-no 5 #t)) diff --git a/collects/scribblings/more/step2.txt b/collects/scribblings/more/step2.txt index 15f3f649b0..48c5fd9396 100644 --- a/collects/scribblings/more/step2.txt +++ b/collects/scribblings/more/step2.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; The `serve' function is revised to run the loop ;; in a thread, and it returns a function to shut down diff --git a/collects/scribblings/more/step3.txt b/collects/scribblings/more/step3.txt index bb0e885aba..0bd0ab5e5e 100644 --- a/collects/scribblings/more/step3.txt +++ b/collects/scribblings/more/step3.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; Only `accept-and-handle' changes, moving the ;; handle work into a thread. diff --git a/collects/scribblings/more/step4.txt b/collects/scribblings/more/step4.txt index 3e83cef1c2..021382df5f 100644 --- a/collects/scribblings/more/step4.txt +++ b/collects/scribblings/more/step4.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; Both `server' and `accept-and-handle' change ;; to use a custodian. diff --git a/collects/scribblings/more/step5.txt b/collects/scribblings/more/step5.txt index 05f231f0e5..0c6f77ed20 100644 --- a/collects/scribblings/more/step5.txt +++ b/collects/scribblings/more/step5.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; New imports: (require xml net/url) diff --git a/collects/scribblings/more/step6.txt b/collects/scribblings/more/step6.txt index 2192f73c3e..4623c9c342 100644 --- a/collects/scribblings/more/step6.txt +++ b/collects/scribblings/more/step6.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; For changes, skip down to `build-request-page', ;; after the line. diff --git a/collects/scribblings/more/step7.txt b/collects/scribblings/more/step7.txt index 629eef89db..5187aec2f8 100644 --- a/collects/scribblings/more/step7.txt +++ b/collects/scribblings/more/step7.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; There's only one change. It's in `accept-and-handle', ;; and it's marked with "<<<". diff --git a/collects/scribblings/more/step8.txt b/collects/scribblings/more/step8.txt index e752942282..e3d36df993 100644 --- a/collects/scribblings/more/step8.txt +++ b/collects/scribblings/more/step8.txt @@ -1,4 +1,4 @@ -#lang scheme +#lang racket ;; No changes to old code --- just three new ;; functions and registrations at the end. diff --git a/collects/scribblings/more/step9.txt b/collects/scribblings/more/step9.txt index 7dbf104abb..5d9c1f3d5e 100644 --- a/collects/scribblings/more/step9.txt +++ b/collects/scribblings/more/step9.txt @@ -1,10 +1,10 @@ -#lang scheme +#lang racket ;; See "<<<" for two small changes, then jump down ;; to `send/suspend'. (require xml net/url - scheme/control) ;; <<< new import + racket/control) ;; <<< new import (define (serve port-no) (define main-cust (make-custodian))