[racket] Stateless servlets and formlets error

47 views
Skip to first unread message

Lorenz Köhl

unread,
Feb 6, 2013, 2:13:37 AM2/6/13
to us...@racket-lang.org
The following servlet show an input field from a formlet and then tries to use formlet-process on the resulting request. I don't understand why this fails when, like below, the send/suspend and formlet-process is in a function but works when inlined, like the #; commented code in handle-html-form.

The error is

stuff-url: Cannot stuff (kont #<procedure:...abort-resume.rkt:154:25>) into a URL because it contains non-serializable pieces. Convert #<procedure:...formlets/lib.rkt:17:2> to a serializable struct

Why does the extra lambda lead to this error message?


#lang web-server
(provide handle-html-form
interface-version)
(define interface-version 'stateless)
(require web-server/formlets web-server/servlet-env)

(define main-input-formlet
(formlet
(#%# ,[(to-string (required (text-input))) . => . text])
text))

(define (send/form2 f)
(formlet-process f
(send/suspend
(lambda (k-url)
(response/xexpr
`(form ([action ,k-url] [method "POST"])
,@(formlet-display f)))))))

(define (handle-html-form req)
(define user-input
(send/form2 main-input-formlet)
#;(formlet-process
main-input-formlet
(send/suspend
(lambda (k-url)
(response/xexpr
`(form ([action ,k-url] [method "POST"])
,@(formlet-display main-input-formlet)))))))
(displayln user-input))

(serve/servlet handle-html-form #:stateless? #t)
____________________
Racket Users list:
http://lists.racket-lang.org/users

Jay McCarthy

unread,
Feb 6, 2013, 9:28:14 AM2/6/13
to Lorenz Köhl, us...@racket-lang.org
On Wed, Feb 6, 2013 at 12:13 AM, Lorenz Köhl <rainbo...@gmail.com> wrote:
> The following servlet show an input field from a formlet and then tries to use formlet-process on the resulting request. I don't understand why this fails when, like below, the send/suspend and formlet-process is in a function but works when inlined, like the #; commented code in handle-html-form.
>
> The error is
>
> stuff-url: Cannot stuff (kont #<procedure:...abort-resume.rkt:154:25>) into a URL because it contains non-serializable pieces. Convert #<procedure:...formlets/lib.rkt:17:2> to a serializable struct
>
> Why does the extra lambda lead to this error message?

The answer to the "WHY" is this:

1. send/form2 is generalized over 'f', so the particular binding for
'f' must be part of its closure, which must be serialized.

2. If you inline it, or replace the f is send/form2 with
'main-input-formlet', then you are now referring to a global variable,
which is never part of closures, because it is always available.

3. The value that you are giving to 'main-input-formlet' is not
serializable, because it is a normal function. This is documented on
the contract for formlets:

http://docs.racket-lang.org/web-server/formlets.html#%28def._%28%28lib._web-server%2Fformlets%2Flib..rkt%29._formlet%2Fc%29%29

Now, the question you didn't ask, does it have to be this way? (1) and
(2) have to work the way that they do. (3) is a little bit harder to
decide if it has to be that way. Serializability is an infectious
property where you can't just rely on your local code to do something,
but you need global changes across the system. The Web server does not
generally promise that its data structures are serializable (for
example, requests are not because they contain promises, which would
be very expensive to make serializable across the system.) The only
"easy" way to make formlets serializable would be to write that file
in the "#lang web-server" language. But down that path is writing
every module in Racket in this language. (For example, objects and
classes aren't generally serializable so the second you start using
them in a Web app you want to be serializable, you're in for a world
of hurt.)

I don't think the benefits are worth it to justify things in the case
of formlets, but I'm open to arguments.

What should you do instead in this and similar situations? This is the
reason for serial->native, etc. You could also change your program so
that the set of valid formlets were always top-level, if that were the
case, and name them by symbol.

Jay

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

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

Lorenz Köhl

unread,
Feb 6, 2013, 11:00:46 AM2/6/13
to Jay McCarthy, us...@racket-lang.org
>> Why does the extra lambda lead to this error message?
>
> The answer to the "WHY" is this:
>
> 2. If you inline it, or replace the f is send/form2 with
> 'main-input-formlet', then you are now referring to a global variable,
> which is never part of closures, because it is always available.

I see, this is what confused me. Thanks for the explanation.

> I don't think the benefits are worth it to justify things in the case
> of formlets, but I'm open to arguments.

I'll make the formlets toplevel.

The other problem I have is calling functions from the servlet that use send/suspend and friends, which expect certain continuation marks as I understand it. Is it possible to fake these for a unit test?

Another way is to modify make-servlet-tester to not redact the response so I can test other properties like return code and headers. But I'd prefer to do it directly.

Lo

Jay McCarthy

unread,
Feb 7, 2013, 5:24:46 PM2/7/13
to Lorenz Köhl, users
On Wed, Feb 6, 2013 at 9:00 AM, Lorenz Köhl <rainbo...@gmail.com> wrote:
>>> Why does the extra lambda lead to this error message?
>>
>> The answer to the "WHY" is this:
>>
>> 2. If you inline it, or replace the f is send/form2 with
>> 'main-input-formlet', then you are now referring to a global variable,
>> which is never part of closures, because it is always available.
>
> I see, this is what confused me. Thanks for the explanation.
>
>> I don't think the benefits are worth it to justify things in the case
>> of formlets, but I'm open to arguments.
>
> I'll make the formlets toplevel.
>
> The other problem I have is calling functions from the servlet that use send/suspend and friends, which expect certain continuation marks as I understand it. Is it possible to fake these for a unit test?

Yes... you would want to use call-with-web-prompt from

https://github.com/plt/racket/blob/master/collects/web-server/lang/abort-resume.rkt

And it should "just work"

> Another way is to modify make-servlet-tester to not redact the response so I can test other properties like return code and headers. But I'd prefer to do it directly.

I think this would be better to do. We just need to change line 24

https://github.com/plt/racket/blob/master/collects/web-server/test.rkt#L24

to support the stateless mode.

And lines 53-57 to allow you to see the currently un-named part of the
bytes (the .+ in the regexp.)

Jay


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

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

____________________

Jay McCarthy

unread,
Feb 8, 2013, 9:02:22 AM2/8/13
to Lorenz Köhl, users
I just pushed a change that allows stateless and headers in web-server/test

Jay

Reply all
Reply to author
Forward
0 new messages