Skip to content
This repository has been archived by the owner. It is now read-only.

The ISSR backend for The Hunchentoot HTTP server.

License

Notifications You must be signed in to change notification settings

interactive-ssr/hunchenissr

Repository files navigation

Hunchenissr

That is HUNCHENtoot ISSR. Make sure to see the main readme for the ISSR project and setup issr.js. ISSR allows you to make interactive web pages without writing client scripting; just include issr.js and generate some HTML in your server language (in the case of Hunchenissr: Common Lisp).

Getting Started

Install with quicklisp. You may want to include some other web tools too. I suggest markup for generating HTML.

(ql:quickload '(hunchenissr markup))

Then include hunchentoot and hunchenissr in your package:

(defpackage your-package
  (:use #:cl #:markup)
  (:import-from #:hunchentoot
                ;; any other hunchentoot functions you might need
                easy-acceptor)
  (:import-from #:hunchenissr
                define-easy-handler
                *id*
                *socket*
                *ws-port*
                start
                stop
                redirect
                -on-connect-hook-
                -on-disconnect-hook-))
(in-package #:your-package)

Start your server:

(defparameter server
  (start (make-instance 'easy-acceptor
                        :port 8080
                        :document-root "resources/")
         :ws-port 4433))

:ws-port becomes a global variable.

Now you can use define-easy-handler just like you would in hunchentoot. Inside define-easy-handler, some new variables will be available:

  • *id*: The unique id to make the connection use is in the setup of issr.js.
  • *ws-port*: The port websockets connect on. Also used in setup of issr.js.
  • *first-time*: t if it is the first HTTP connection of this client.
  • *socket*: The current connection. It is nil on the first HTTP connection.
  • -clients- : A hash-table of all connections. Key: sockets, Value: (list hunchentoot-request previous-virtual-dom)

Mostly everything works just like regular Hunchentoot with the following caveats:

  • Cookies not sent over and initial HTTP request must be :http-only nil or they will not be saved.
  • Since session uses cookie, if you want to use session, then you must call (start-session) on the initial HTTP request because you don’t want the session cookie being not HTTP only.
  • You have the hunchenissr:rr function which can Re-Render a different client with some parameters (optional). This allows the advantages of websockets without the complexity of actually programming any. This doesn’t cover all the websocket use cases, but it covers some basic ones.

In addition to the back-end hunchenissr:rr function, there are two hooks: -on-connect-hook- and -on-disconnect-hook-. These hooks work like hooks in Emacs: they are each a list of functions; the functions should take a socket as their only parameter. Use pushnew to add functions to the hooks (best not to use anonymous functions). All functions in -on-connect-hook- are called right after a socket connects, so it’s request and previous page will be present in -clients- . All functions in -on-disconnect-hook- are run right before the socket disconnects so its request is also available in -clients-.

Example with Easy Handler and Markup

Enable Markup’s HTML syntax. You could use something like CL-WHO instead.

(markup:enable-reader)

Define Easy Handler with ISSR.

This example is just an unordered list of consecutive numbers. The “add” button makes the list longer, and the “delete” button makes the list shorter (minimum length is 0).

(define-easy-handler (example :uri "/example")
    ((x :init-form "0"))
  (setq x (parse-integer x))
  (write-html
   <html>
     <head>
       <script src="/issr.js"></script>
       <script noupdate="t">
         ,(format nil "connect(~a, 'ws', ~a)" *id* *ws-port*)
       </script>
       <title>Easy Handler Example</title>
     </head>
     <body>
       <ul>
         <!-- Comma and ,@ are to run Lisp code and dump the result into HTML -->
         <!-- ,@ is to dump a list rather than a value -->
         ,@(loop for n from 1 to x
             collect <li>,(progn n)</li>)
       </ul>
       <button onclick="rr(this)" action="x" value=(+ x 1)>
         add
       </button>
       <button onclick="rr(this)" action="x" value=(max 0 (- x 1))>
         delete
       </button>
     </body>
   </html>))

Error Handling

Hunchenissr uses Hunchentoot and Portal for communications with the client.

To enter the debugger for errors on the initial connection:

(setq hunchentoot:*catch-errors-p* nil)

To show errors to the client on the initial connection:

(setq hunchentoot:*show-lisp-errors-p* t)

To *not* enter the debugger on subsequent errors:

(setq pws:*debug-on-error* nil)

To show errors in the JavaScript console.error on subsequent errors:

(setq hunchenissr:*show-errors-to-client* t)

Issues

If you find any bugs of have any issues (installing, using, questions) use the Github issue tracker.

Contact

If you want to contact me about collaboration, licensing, implementing a new back-end, etc. You can use the email in hunchenissr.asd.

About

The ISSR backend for The Hunchentoot HTTP server.

Resources

License

Stars

Watchers

Forks