\documentclass[a4paper]{article}

\usepackage[latin1]{inputenc}

\usepackage{a4wide}

\begin{document}

\thispagestyle{empty}
\title{lisp-cgi-utils, HTTP cookie support documentation}
\author{Alexander Schreiber $<als@thangorodrim.de>$}
\maketitle

Documentation of the HTTP cookie support for the
\texttt{lisp-cgi-utils} package.
\thispagestyle{empty}
\pagebreak

\section{HTTP cookie support}

Since \texttt{lisp-cgi-utils} is a server-side package, things are
viewed from the server side in the documentation below. The are two
parts to the cookie support: sending cookies and receiving them. Both
are described separately in detail below.

\subsection{Sending cookies}

Since cookies are sent as part of the HTTP headers of a HTTP request,
all cookie sending must be done before sending the HTTP headers. The
function for sending cookies is called \texttt{http-add-cookie}, with
the parameters according to figure \ref{fig:http-add-cookie-parm} and
an usage example in figure \ref{fig:http-add-cookie-example}.

\begin{figure}[htbp]
  \centering
  \texttt{(http-add-cookie name value [Comment Domain Max-Age Path
  Secure Version])}
  \begin{itemize}
  \item \texttt{name} and \texttt{value} are required parameters, they
    contain the name and the value of the cookie to be sent,
  \item \texttt{Comment} - a  comment for the cookie; optional, string,
  \item \texttt{Domain} - the domain for which the cookie is valid;
    optional, string,
  \item \texttt{Max-Age} - maximum age of the cookie, in seconds;
    optional, number,
  \item \texttt{Path} - the path for which the cookie is valid;
    optional, string,
  \item \texttt{Secure} - wether the cookie should only be transmitted
    over a secure connection; optional, boolean, default \texttt{nil},
  \item \texttt{Version} - the version of the cookie specification;
    optional, number, default 1, modify at your own peril (it breaks
    the standard!) 
  \end{itemize}
  \caption{\texttt{http-add-cookie} - parameters}
  \label{fig:http-add-cookie-parm}
\end{figure}

\begin{figure}[htbp]
  \centering
\begin{verbatim}
(http-add-cookie "application" "shopping basket" :Path "/appserv/")
(http-send-headers)
(princ (html:html-header "shopping basket"))
(princ (html:body
        (html:h1 "Your shopping basket")
        (html:p "Reload this page to go shopping ...")))
(princ (html:html-footer))
\end{verbatim}
  \caption{\texttt{http-add-cookie} - example}
  \label{fig:http-add-cookie-example}
\end{figure}

\subsection{Receiving cookies}

To read cookies sent by the client you can read the list of all
cookies and access the value of a named cookie. The function to get
the list of all cookies sent by the client is named
\texttt{http-cookie-list} and documented in figure
\ref{fig:http-cookie-list}, the function for accessing the value of a
single name cookie, \texttt{http-cookie} is documented in figure
\ref{fig:http-cookie}. Figure \ref{fig:read-cookies} shows an example
for both.
\begin{figure}[htbp]
  \centering
  \texttt{(http-cookies-list)} \\
  Returns a list of all cookies sent by the client, the list will be
  empty if the client sent no cookies.
  \caption{\texttt{http-cookie-list}}
  \label{fig:http-cookie-list}
\end{figure}

\begin{figure}[htbp]
  \centering
  \texttt{(http-cookie name)} \\
Returns the value of the cookie \texttt{name}. The cookie name is case
insensitive, as specified in the RFCs. Returns \texttt{nil} if no
matching cookie can be found.
  \caption{\texttt{http-cookie}}
  \label{fig:http-cookie}
\end{figure}

\begin{figure}[htbp]
  \centering
  \begin{verbatim}
(defun show-cookies ()
  "show all received cookies"
  (concatenate 'string
               (html:h2 "cookies sent by user agent")
               (html:table '((border . "1"))
                (html:tr
                 (html:th "name")
                 (html:th "value"))
                (apply #'concatenate 'string
                       (mapcar #'(lambda (name)
                                   (html:tr
                                    (html:td
                                     (html:code name))
                                    (html:td
                                     (html:code (http:http-cookie name)))))
                               (http:http-cookie-list))))))
\end{verbatim}
  \caption{Example for showing all cookies sent by the client}
  \label{fig:read-cookies}
\end{figure}
\section{Standards \dots does anybody follow them?}

\subsection{Relevant RFCs}

There are several standards that are of relevance for the HTTP cookie
support as implemented in the \texttt{lisp-cgi-utils} package for
Lisp:

\begin{itemize}
\item RFC2616: Hypertext Transfer Protocol -- HTTP/1.1, specifying the
  basic HTTP protocol,
\item RFC2109: HTTP State Management Mechanism, specifying HTTP state
  management using cookies,
\item RFC2965: HTTP State Management Mechanism, specifying HTTP state
  management using cookies, supersedes RFC2109.
\end{itemize}

\subsection{Anybody reading any RFCs lately?}

The first version of the HTTP cookie support for the
\texttt{lisp-cgi-utils} package was implemented along RFC2965, since
the author naively assumed that it would be best to follow the most
current RFC. \emph{This turned out not to be the case.} RFC2965
specifies the \texttt{Set-Cookie2}-Header which was happily ignored by
the authors standard test browser, Firefox. Switching to the
\texttt{Set-Cookie}-Header as specified by RFC2109 resulted in things
suddenly working. So much for basing your implementation on the
newest\footnote{At the time of this writing, RFC2965 was almost 4 and
RFC2109 7 years old.} standard.

\subsection{We don't care, we don't read those RFCs anyway}

That wasn't the last surprise and the next one is really annoying. The
relevant RFCs specify the value of a cookie to be either a token (a
string of characters not containing whitespace or special characters) or
a quoted string, with semicolon separating the entries of the
\texttt{Set-Cookie}- or \texttt{Set-Cookie2}-Header. Parsing this
format can be done with a very simple state machine. That was
obviously way too complex for some browser programmers and so they
simply decided to chop the entries apart at every semicolon. The
result of this stupidity is that if a cookie value, written as a
quoted string, contains a semicolon, some browser will chop off
everything behind the semicolon, while keeping the starting quote
character, resulting in the breakage shown in figure \ref{fig:cookie-parser}.

\begin{figure}[htbp]
  \centering
  \begin{enumerate}
  \item The server sends: \\
    \texttt{Set-Cookie: foo="bar;baz"; quux=xyzzy; Version=1}
  \item The broken browser breaks the items at the first semicolon,
    reading the cookie as: \\
    \texttt{foo="bar}
  \item The broken browser sends back to the server: \\
    \texttt{Cookie: foo="bar; quux=xyzzy}
  \item The server side, parsing according to the RFCs, reads the
    cookie(s) as: \\
    \texttt{foo = "bar; quux=xyzzy"}
  \end{enumerate}
  Great, isn't it?
  \caption{Broken cookie parsing}
  \label{fig:cookie-parser}
\end{figure}

The obvious work-around for this is to avoid sending cookies
containing semicolons. Fortunately, most browsers do the right thing
and work correctly. But since a significant percentage of users out
there is using broken browsers, one has to program with this breakage
in mind.

\subsection{List of fools}

At the time of this writing, the following browsers exhibit the
abovementioned broken behaviour of not properly reading quoted-string
cookie values with embedded semicolons:
\begin{itemize}
\item Internet Explorer (all tested versions, including
  current)\footnote{No, I'm not the least surprised here.},
\item Konqueror, the browser of the KDE project (versions from KDE2
  and KDE3, including current)\footnote{Looks they are \emph{really}
  faithful about being bug-for-bug compatible with the Windows side.}
\item Safari, the browser of Mac OS X (including current) - no
  surprise here, since it uses the Konqueror core,
\item links, a terminal based browser (only version 0.96 tested, more
  tests needed)
\end{itemize}

\end{document}
