Can I somehow connect to and update values in a specific hash table used by a running web server?

29 views
Skip to first unread message

Marc Kaufmann

unread,
Dec 21, 2019, 4:44:26 AM12/21/19
to Racket Users
Hi all,

one useful feature of the Django web framework is that it is easy to access the database and change it on the fly. I am not using a DB, but a hash inside of Racket for various reasons. I understand that it would be easy to connect to a database in any language, and getting the hash is a different beast - but I am wondering if there is an easy way such that I could tell the racket web server via a command line or REPL interaction "(update-user uid key-name new-value)" or some such.

Is that easily possible? (And very secondarily: Is this a stupid idea? But even if it is, it's what I am under time pressure to get working, as a proper solution ain't gonna happen in time.)

I guess I could add some function to the server that gets called via a specific url that writes to some file, which I then edit manually (or via the REPL) and which gets read in again. A bit cumbersome, but I've done worse.

Cheers,
Marc

George Neuner

unread,
Dec 21, 2019, 4:13:54 PM12/21/19
to Marc Kaufmann, racket users

On 12/21/2019 4:44 AM, Marc Kaufmann wrote:
one useful feature of the Django web framework is that it is easy to access the database and change it on the fly. I am not using a DB, but a hash inside of Racket for various reasons. I understand that it would be easy to connect to a database in any language, and getting the hash is a different beast - but I am wondering if there is an easy way such that I could tell the racket web server via a command line or REPL interaction "(update-user uid key-name new-value)" or some such.

Is that easily possible? (And very secondarily: Is this a stupid idea? But even if it is, it's what I am under time pressure to get working, as a proper solution ain't gonna happen in time.)

Running a REPL inside your program is fairly easy but not terribly safe.  E.g., you can dedicate a thread to reading a network port and executing whatever code fragments you send.  But you need to take precautions, limiting what it is allowed to do, and who can access it, so unauthorized users can't screw up your program.

In my apps I make hot tweak settings available through a secured web interface.  The "variables" are functions exported from my configuration module (similar to the way parameters work).
(define-syntax getter/setter!
  (syntax-rules ()
    ((getter/setter!)
     ; -- start template

     (let [(var null)]
       (lambda x
         (cond
           ([null? x] var)
           ([pair? x] (set! var (car x)))
           (else (error))
           ))
       )
    
     ; -- end template
     )))

(define some-config-var (getter/setter!))

Then  (some-config-var <value>)  changes the value, and  (some-config-var)  gets the current value.

It's simplistic, but it works well.  I haven't run into the need for more explicit synchronization, but if needed it would be simple to add a semaphore / mutex to the macro.

During program execution I can tweak these "variables" using a special HTML page that lets me review and set many variables all at once.  But it could be done using a bunch of simple handlers - one per variable - that could be command line driven using curl.


In your case, you could create an "update-user" URL handler that updates your hash from its arguments.  If you give it an hard to guess name and don't document it, then it may be reasonably safe.

YMMV,
George

Matt Jadud

unread,
Dec 21, 2019, 9:25:58 PM12/21/19
to George Neuner, Marc Kaufmann, racket users
My off-list thought (which was largely because I wasn't sure it would even work) was to run a second web server on another port that was bound to localhost. Then, SSH onto the localhost when needed, and squirt values into the locally bound webserver as needed. Depending on the server config (e.g., you're the only one on it) would make this reasonably secure. (If someone got onto your host, you probably aren't worrying about the hash table of parameters...)

Cheers,
Matt

#lang racket
(require web-server/dispatch
         web-server/servlet
         web-server/servlet-env)

(define posts (make-hash))

(define (list-posts req)
  (response/xexpr
   `(html
     (body
      ,@(for/list ([(k v) posts])
          `(p ,(format "[~a] ~a" k v)))
   ))))

(define (review-post req key)
  (hash-ref posts key))

(define-values (blog-dispatch blog-url)
    (dispatch-rules
     [("") list-posts]
     [("posts" (string-arg)) review-post]
     [else list-posts]))

(define (add-key req k v)
  (hash-set! posts k v)
  (response/xexpr `(html (body ,(format "Added '~a' -> '~a'" k v))))
  )

(define (get-key req k)
  (response/xexpr `(html (body ,(format "Found '~a'"   (hash-ref posts k false)))))
  )

(define-values (tweaker url)
  (dispatch-rules
   [("set" (string-arg) (string-arg)) add-key]
   [("get" (string-arg)) get-key]))

(thread (λ ()
          (serve/servlet blog-dispatch
                         #:servlet-path ""
                         #:servlet-regexp #rx""
                         #:port 8080
                         #:listen-ip "0.0.0.0")))
(thread (λ ()
          (serve/servlet tweaker
                         #:servlet-path ""
                         #:servlet-regexp #rx""
                         #:port 9090
                         #:listen-ip "127.0.0.1")))


--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/b3a4de57-9b40-faa3-651b-b5efa08329a4%40comcast.net.

Marc Kaufmann

unread,
Dec 22, 2019, 4:44:21 AM12/22/19
to Matt Jadud, George Neuner, racket users
Thanks George and Matt, those look great.

I think I'll try to implement your version later George, as the syntax-parse makes me sure I'll screw up. However it looks like a really nice way to contain what can be changed/updated, while adding things flexibly to it. And it should be possible to combine with Matt's idea of running it only on a second servlet that is on a separate port (to avoid the 'you can only do this on <super-sicrit-url>/update-values').

Cheers,
Marc

Philip McGrath

unread,
Dec 22, 2019, 4:14:07 PM12/22/19
to Marc Kaufmann, George Neuner, Matt Jadud, racket users
The other advice here is good and could help to implement a more principled solution. But, if you want to do something equivalent to editing a running application's database on the fly, just using Racket values instead of a DB, I would build on `file-box`: https://docs.racket-lang.org/web-server/stateless.html#%28mod-path._web-server%2Flang%2Ffile-box%29

(I can elaborate later if that would be helpful.)

-Philip

George Neuner

unread,
Dec 23, 2019, 9:59:48 AM12/23/19
to Matt Jadud, Marc Kaufmann, racket users

On 12/21/2019 9:25 PM, Matt Jadud wrote:
> My off-list thought (which was largely because I wasn't sure it would
> even work) was to run a second web server on another port that was
> bound to localhost. Then, SSH onto the localhost when needed, and
> squirt values into the locally bound webserver as needed. Depending on
> the server config (e.g., you're the only one on it) would make this
> reasonably secure. (If someone got onto your host, you probably aren't
> worrying about the hash table of parameters...)

Certainly you can run a 2nd web server on a different port, but IMHO
that is overkill.

I use shared secrets to secure my setting page:  a hidden parameter
whose value is a cryptohash of a shared pass-phrase.  If that parameter
isn't present, the web server returns "unknown URL" rather than the
setting page.

George
Reply all
Reply to author
Forward
0 new messages