Consulting parameters in #lang web-server servlets

36 views
Skip to first unread message

Philip McGrath

unread,
Jun 22, 2017, 3:56:11 AM6/22/17
to Racket Users
I'm encountering some strange behavior when using parameters with my (stateless) web servlets.

I want to check the value of a cookie every time a request is made to my server and make a Racket value derived from the cookie's value available for use within the servlet code. Since this value shouldn't be stored in the continuation — it should be generated fresh for every request — it seemed like writing a dispatcher (in the sense of serve/launch/wait, not of dispatch-rules) was the right place to do this. Since the servlet code itself does not need to (and indeed should not) modify the value, I thought I could use a standard parameter to store the value. Essentially I have a function shaped like this:
(define ((wrap-dispatcher inner) connection request)
  (parameterize ([my-param ...])
    (inner connection request)))
where inner is a dispatcher produced by dispatch/servlet.

All of the work being done by wrap-dispatcher seems to be happening correctly (e.g. setting and reading the cookie), but within the servlet itself, the parameter seems to get "stuck" on the first value installed by wrap-dispatcher after the server is launched (which is not the default value for the parameter). 

I'm not sure why this is happening. I understand that plain parameters cannot be set within stateless servlets for serialization reasons (hence the existence of web-parameters), but I didn't think that impacted code that merely accesses the value of a parameter.

More urgently, I'm not sure how to fix it. Would making my-param a web parameter solve the problem (without storing the value in the continuation)? Or is there some other method that should be used for this sort of per-request work?

Thanks,
Philip

Jay McCarthy

unread,
Jun 22, 2017, 9:39:45 AM6/22/17
to Philip McGrath, Racket Users
Hi Philip,

I think I'd have to see more code to really tell what the problem is,
but here are some thoughts:

- A continuation captures the values of parameters at the time it is
captured. So, if your parameterize is outside of the dispatcher that
calls the continuation, then the code inside the continuation will not
see the parameter change. That's a pretty important feature of
continuations.

- Web parameters would work the same way as normal parameters and not help you.

- The stateless Web language guarantees that the parameters will have
values they had when the servlet was initialized.

- I think that you may want to use a thread-cell as the storage place.

Jay
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-users...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
-=[ Jay McCarthy http://jeapostrophe.github.io ]=-
-=[ Associate Professor PLT @ CS @ UMass Lowell ]=-
-=[ Moses 1:33: And worlds without number have I created; ]=-

Philip McGrath

unread,
Jun 22, 2017, 10:21:40 AM6/22/17
to Jay McCarthy, Racket Users
I've come up with a (relatively) minimal example.

In "common.rkt":
#lang racket
(provide my-param)
(define my-param
  (make-parameter #f))

In "servlet.rkt":
#lang web-server
(require "common.rkt")
(provide start)
(define (start req)
  (response/xexpr
   `(html (head (title "Example"))
          (body (p ,(format "~v" (my-param)))))))

In "run.rkt":
#lang racket
(require "common.rkt"
         "servlet.rkt"
         web-server/http
         web-server/servlet-dispatch)

(define ((wrap-dispatcher inner) connection request)
  (parameterize ([my-param (request-uri request)])
    (inner connection request)))
(serve/launch/wait (λ (quit-sema)
                     (wrap-dispatcher
                      (dispatch/servlet start
                                        #:stateless? #t)))
                   #:banner? #t)

If you visit a bunch of different URLs, the page will always show the first URL you visited.

From what you said, I'm guessing the problem is that "the stateless Web language guarantees that the parameters will have values they had when the servlet was initialized", which I was not aware of.

I haven't worked with thread cells before, if that is indeed where I want to store this data. One difference that immediately occurs to me is that the value would not be propagated to any new child threads, right?

Thanks,
Philip

-Philip

Jay McCarthy

unread,
Jun 22, 2017, 10:31:03 AM6/22/17
to Philip McGrath, Racket Users
Yup

> I haven't worked with thread cells before, if that is indeed where I want to
> store this data. One difference that immediately occurs to me is that the
> value would not be propagated to any new child threads, right?

If you want it propagated, then create the cell with #t for preserved?

http://docs.racket-lang.org/reference/threadcells.html?q=thread-cell#%28def._%28%28quote._~23~25kernel%29._make-thread-cell%29%29
>> > email to racket-users...@googlegroups.com.

Philip McGrath

unread,
Jun 22, 2017, 3:07:16 PM6/22/17
to Jay McCarthy, Racket Users
Ok, I think I've got it, but just to make sure I'm not missing any subtleties, or else for the benefit of posterity, here's the version with thread cells.


In "common.rkt":
#lang racket

(provide my-val
         call-with-my-val)

(define cell:my-val
  (make-thread-cell #f #t))

(define (my-val)
  (thread-cell-ref cell:my-val))

(define (call-with-my-val new thunk)
  (let ([old (my-val)])
    (dynamic-wind (λ () (thread-cell-set! cell:my-val new))
                  thunk
                  (λ () (thread-cell-set! cell:my-val old)))))

In "servlet.rkt":
#lang web-server

(require "common.rkt")

(provide start)

(define (start req)
  (response/xexpr
   `(html (head (title "Example"))
          (body (p ,(format "~v" (my-val)))))))

In "run.rkt":
#lang racket

(require "common.rkt"
         "servlet.rkt"
         web-server/http
         web-server/servlet-dispatch)

(define ((wrap-dispatcher inner) connection request)
  (call-with-my-val (request-uri request)
                    (λ () (inner connection request))))

(serve/launch/wait (λ (quit-sema)
                     (wrap-dispatcher
                      (dispatch/servlet start
                                        #:stateless? #t)))
                   #:banner? #t)

Thanks so much!

Philip


-Philip

Reply all
Reply to author
Forward
0 new messages