[racket] Lazy Request Handler

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Wed Mar 21 21:28:19 EDT 2012

Since you did not tell us what error you had, I assume the error was:

serve/servlet: contract violation, expected: can-be-response?, given: #<promise>

When I tried your code, I also had the error:

current-directory: `exists' access denied for /home/jay/Downloads/

because you need to give the sandbox permission to read, like:

#:allow-read (list "/")

But I assume that you know that error doesn't have to do with the Web server.

Returning to the serve/servlet error...

If you change your 'start' to:

(define (start request)
  (define prm ((lazy-eval 'lazy-handler) request))
  (printf "~a ~a\n" prm (promise? prm))
  (define ans (force prm))
  (printf "~a\n" ans (promise-forced? prm))
  ans)

You will see the output:

#<promise> #f
Servlet (@ /main) exception:
promise-forced?: expected argument of type <promise>; given: #<promise>

This indicates the thing returned by your handler is not a promise and
therefore cannot be forced.

This is called structure generativity: every instantiation of a module
has different structures, and sandboxes lead to multiple
instantiations of the same module---racket/promise in this case. Thus,
different 'promise' data structures.

Fixing this is what 'sandbox-namespace-specs' is for.

If you wrap your call to 'make-evaluator' in:

  (parameterize ([sandbox-namespace-specs
                  (list sandbox-make-namespace
                        'racket/promise
                        'web-server/http)])
 .....)

Then it will work. This shares the modules 'racket/promise' (to get
the right promise data structure) and 'web-server/http' (to get the
right request and response structures) between the evaluator and the
host Racket program.

In case you don't realize, using an evaluator is totally unnecessary.
You can just write another module in the lazy language and require it
in the strict program and call force normally. For example:

#lang racket/load

(module lazy-handler lazy
  (require web-server/http/bindings
           web-server/http/response-structs
           web-server/http/xexpr)
  (define (lazy-handler request)
    (let ((bindings (request-bindings request)))
      (if (exists-binding? 'hi bindings)
        (response/xexpr "Hi!")
        (response/xexpr ""))))
  (provide lazy-handler))

(module the-server racket
  (require web-server/servlet-env
           web-server/http/bindings
           web-server/http/response-structs
           web-server/http/xexpr
           'lazy-handler)

  (define (start request)
    (define prm (lazy-handler request))
    (printf "~a ~a\n" prm (promise? prm))
    (define ans (force prm))
    (printf "~a ~a\n" ans (promise-forced? prm))
    ans)

  (serve/servlet start
                 #:launch-browser? #t
                 #:quit? #f
                 #:listen-ip #f
                 #:servlet-path "/main"
                 #:port 8080
                 #:servlet-regexp #rx"main.*"
                 #:extra-files-paths
                 (list (build-path (current-directory)))))

(require 'the-server)

Hope this helps,

Jay



On Wed, Mar 21, 2012 at 11:44 AM, Nathan Breit <nabreit at gmail.com> wrote:
> I'm trying to write a servlet that plugs into the existing server. Here's an
> example:
>
> #lang racket
> (require web-server/servlet-env)
> (require web-server/http/bindings)
> (require web-server/http/response-structs)
> (require web-server/http/xexpr)
>
> (require racket/sandbox)
> (define lazy-eval (make-evaluator 'lazy))
>
> (map lazy-eval
>      '(
>        (require web-server/http/bindings)
>        (require web-server/http/response-structs)
>        (require web-server/http/xexpr)
>        (require racket/promise)
>        (define (lazy-handler request)
>          (let ((bindings (request-bindings request)))
>            (if (exists-binding? 'hi bindings)
>                (response/xexpr "Hi!")
>                "")))
>        ))
>
> (define (start request)
>   (force ((lazy-eval 'lazy-handler)
>           request)))
>
> (serve/servlet start
>                #:launch-browser? #f
>                #:quit? #f
>                #:listen-ip #f
>                #:servlet-path ""
>                #:port 8080
>                #:servlet-regexp #rx"main.*"
>                #:extra-files-paths
>                (list (build-path (current-directory) "extraFiles")))
>
> On Wed, Mar 21, 2012 at 6:22 AM, Matthias Felleisen <matthias at ccs.neu.edu>
> wrote:
>>
>>
>>
>> On Mar 21, 2012, at 5:18 AM, Nathan Breit wrote:
>>
>> > Hi,
>> > I'm trying to implement a Racket web-server handler that does lazy
>> > evaluation. My approach so far as been to try making a evaluator/namespace
>> > that uses the lazy racket, then evaluate a function in it that returns a
>> > lazy request handler. However, I'm running into problems getting the handler
>> > to read the request object. My last resort will be to make a request
>> > serializer and pass serialized requests into the handler, but is there a
>> > better way?
>> > Thanks,
>> > -Nathan
>> > ____________________
>> >  Racket Users list:
>> >  http://lists.racket-lang.org/users
>>
>>
>>
>> Are you trying to write a web server in Lazy or are you trying to write a
>> servlet in Lazy and plug it into the existing strict server? Perhaps you
>> want to post a code snippet that shows where things fail. -- Matthias
>>
>
>
> ____________________
>  Racket Users list:
>  http://lists.racket-lang.org/users
>



-- 
Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University
http://faculty.cs.byu.edu/~jay

"The glory of God is Intelligence" - D&C 93


Posted on the users mailing list.