Search code examples
apachehaskellcgiyesodhaskell-wai

Serving a yesod application as a (Fast)CGI


I'm trying to serve a yesod application as a CGI (or FastCGI) program but Apache shows me a 500 error:

[Mon Sep 21 17:35:41.425565 2020] [http:error] [pid 2758] [client 10.0.2.2:43872] AH02429: Response header name '21/Sep/2020' contains invalid characters, aborting request

Starting with a new template with stack new project yesodweb/sqlite, I've tried to convert it to a CGI program by changing src/Application.hs.

I've imported the Wai CGI library with

import Network.Wai.Handler.CGI              (run)

and changed the last line of appMain to run app:

-- | The @main@ function for an executable running this site.
appMain :: IO ()
appMain = do 
    -- Get the settings from all relevant sources
    settings <- loadYamlSettingsArgs
        -- fall back to compile-time values, set to [] to require values at runtime
        [configSettingsYmlValue]
 
        -- allow environment variables to override
        useEnv
 
    -- Generate the foundation from the settings
    foundation <- makeFoundation settings
 
    -- Generate a WAI Application from the foundation
    app <- makeApplication foundation
                                             
    -- Run the application with Warp
    --runSettings (warpSettings foundation) app
    run app

I thought this was the minimal change to serve it as a CGI program but apparently is not working.

I've already looked into yesod's book chapter on deploying your Webapp which has been handy to write the apropriate Apache configuration so the server could ran the app. It says nothing about the nedded changes to the app code which I presume is where the problem is.

I've also checked this stackoverflow question but it's from almost 8 years ago so it's outdated now.


Solution

  • The problem is that the default makeFoundation logs to standard output, which is also where the CGI program is supposed to send its response, so you're getting intermingling of response headers with log output, and Apache tries to parse log lines as HTTP headers, etc.

    If you replace newStdoutLoggerSet with newStderrLoggerSet, it should work, and the log output will end up in Apache's "error.log" or equivalent.