eudoxia0 / lucerne

A web framework for Common Lisp, built on Clack
http://borretti.me/lucerne/
142 stars 19 forks source link

Respond should provide parameter for HTTP headers #17

Open wnortje opened 8 years ago

wnortje commented 8 years ago

It will be useful if respond can be used to set custom HTTP headers. At the moment I have to use a custom respond-like function everywhere I need to return custom headers.

This will also address my original question in #13.

eudoxia0 commented 8 years ago

You're right. I will do this at the earliest.

wnortje commented 8 years ago

Thank you.

Wimpie Nortje Strongroom - Encrypted Photo Backup https://strongroom.me[1] Selective Share - Encryption-as-a-Service https://www.selectiveshare.com[2]

On Fri, Jan 29, 2016, at 01:30, Fernando Borretti wrote:

You're right. I will do this at the earliest.

— Reply to this email directly or view it on GitHub[3].

Links:

  1. https://strongroom.me/?pk_campaign=email-sig
  2. https://www.selectiveshare.com/?pk_campaign=email-sig
  3. https://github.com/eudoxia0/lucerne/issues/17#issuecomment-176479518
defunkydrummer commented 7 years ago

@wnortje (CC: @eudoxia0)

The response is taken by Lucerne which in turn sends the response to Clack. Clack then adapts the response headers/etc to send them to the particular server, for example Huntchentoot.

If you want to send additional information on the headers, here is where you need to put them: https://github.com/eudoxia0/lucerne/blob/master/src/http.lisp Line 19

(defun respond (body &key (type "text/html;charset=utf-8") (status 200))
  "Construct a response from a @cl:param(body), content @cl:param(type) and
@cl:param(status) code."
  (list status
        (list :content-type type)
        (typecase body
          (string (list body))
          (otherwise body))))

Of course, then modify the calls to respond() : For example you can pass more arguments to respond() in render-template by modifying: line 52 of the same file:


(defmacro render-template ((template) &rest args)
  "Render a Djula template @cl:param(template-name) passing arguments
@cl:param(args)."
  `(respond (djula:render-template* ,template
                                    nil
                                    ,@args)))

The list where there is only one :content-type keyword-value pair, is a property list (plist) and can have more information that will be then sent on the header. For example :location, :content-length, :set-cookie. Now, this will be then sent to Clack and Clack will then adapt the header key-values to what the actual server used (i.e. Huntchentoot) will use.

For example, for huntchentoot: https://github.com/fukamachi/clack/blob/master/src/handler/hunchentoot.lisp Line 135-146:

(defun handle-response (res)
  "Convert Response from Clack application into a string
before passing to Hunchentoot."
  (let ((no-body '#:no-body))
    (flet ((handle-normal-response (res)
             (destructuring-bind (status headers &optional (body no-body)) res
               (setf (return-code*) status)
               (loop for (k v) on headers by #'cddr
                     if (eq k :set-cookie)
                       do (rplacd (last (headers-out*))
                                  (list (cons k v)))
                     else if (eq k :content-type) do
                       (setf (content-type*) v)
                     else if (eq k :content-length) do
                       (setf (content-length*) v)
                     else if (header-out k) do
                       (setf (header-out k)
                             (format nil "~A, ~A" (header-out k) v))
                     else
                       do (setf (header-out k) v))

As you can see, other http header keywords should be accepted. In theory, any keyword. Huntchentoot docummentation:

http://weitz.de/hunchentoot/ HEADER-OUT returns the outgoing http header named by the keyword name if there is one, otherwise NIL. SETF of HEADER-OUT changes the current value of the header named name. If no header named name exists, it is created. .

Regards, Flavio.