koldfront

Haskell WAI middleware to remove a header #feedbase #haskell #programming

Fe The website of Feedbase is a Haskell application, built using the Spock framework. It's my first Haskell program that does something non-trivial in the "real" world.

Recently I was mucking about with cookies in another context, and noticed that the Feedbase website sets a cookie, spockcookie.

This is part of Spock's built-in session handling. I don't use sessions, so I'd rather not set the cookie (given the EU's weird cookie rules and what have you).

You can't turn them off in Spock easily, so I started thinking that maybe some "middleware" could be used.

After some searching I found a StackOverflow question, and combined with looking at the source code of the Network.WAI.Middleware.Gzip module I cobbled together my own little "NoCookies" module:

{-# LANGUAGE OverloadedStrings #-}
module NoCookies where
 
-- Remove all Set-Cookie headers on responses.
 
import Network.Wai (Middleware)
import Network.Wai.Internal (Response(..))
import Network.HTTP.Types (Header)
 
-- Function to hook into middleware:
nocookies :: Middleware
nocookies application request sendResponse = application request $ sendResponse . removeHeader
 
-- Handle all the various kinds of responses:
removeHeader :: Response -> Response
removeHeader (ResponseFile s h b1 b2) = ResponseFile s (filterSetCookie h) b1 b2
removeHeader (ResponseBuilder s h b) = ResponseBuilder s (filterSetCookie h) b
removeHeader (ResponseStream s h b) = ResponseStream s (filterSetCookie h) b
removeHeader r@(ResponseRaw _ _) = r
 
-- Remove Set-Cookie from headers:
filterSetCookie :: [Header] -> [Header]
filterSetCookie hs = filter notSetCookie hs
  where
    notSetCookie (x, _) = x /= "Set-Cookie"

A couple of odd things: the Network.HTTP.Types module defines a bunch of constants for headers, but not hSetCookie.

As you can see, most of my module consists of lines pattern matching the Response type - it seems odd that there should be no smarter way of doing this.

As usual in Haskell the reader of documentation is assumed to always know how to put things together. Unfortunately I don't always know that. So here's how I added my module to the Main.hs of my application:

 ...
import NoCookies
 
app :: SpockCtxM ctx Pg.Connection session state ()
app = do
  logger <- liftIO $ mkRequestLogger def { outputFormat = Apache FromFallback }
  middleware logger
  middleware $ gzip def
  middleware (staticPolicy (addBase "static"))
  middleware nocookies
   ...

Add comment?

Title:

Name:

Email (won't be displayed online):

Text:

0.0241 s
webcustodian@koldfront.dk