]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/Interaction.hs
Bugfix regarding HEAD requests
[Lucu.git] / Network / HTTP / Lucu / Interaction.hs
index e871159ada06c278078b8d29f8fb61aaec2ca8a2..7c43f96c5e7a12bcf3145771a7ea6b4976fed17e 100644 (file)
@@ -1,5 +1,6 @@
 {-# LANGUAGE
-    DeriveDataTypeable
+    CPP
+  , DeriveDataTypeable
   , ExistentialQuantification
   , OverloadedStrings
   , RecordWildCards
@@ -8,6 +9,7 @@
 module Network.HTTP.Lucu.Interaction
     ( Interaction(..)
     , SomeInteraction(..)
+    , EndOfInteraction(..)
 
     , SyntacticallyInvalidInteraction(..)
     , mkSyntacticallyInvalidInteraction
@@ -23,7 +25,6 @@ module Network.HTTP.Lucu.Interaction
     , InteractionQueue
     , mkInteractionQueue
 
-    , setResponseStatus
     , getCurrentDate
     )
     where
@@ -31,12 +32,13 @@ import Blaze.ByteString.Builder (Builder)
 import Control.Applicative
 import Control.Concurrent.STM
 import Data.Ascii (Ascii)
-import qualified Data.ByteString as Strict
+import Data.ByteString (ByteString)
+import Data.Convertible.Base
 import Data.Monoid.Unicode
+import Data.Proxy
 import Data.Sequence (Seq)
-import qualified Data.Strict.Maybe as S
 import Data.Time
-import qualified Data.Time.HTTP as HTTP
+import Data.Time.Format.HTTP
 import Data.Typeable
 import Network.Socket
 import Network.HTTP.Lucu.Config
@@ -45,7 +47,11 @@ import Network.HTTP.Lucu.Headers
 import Network.HTTP.Lucu.Preprocess
 import Network.HTTP.Lucu.Request
 import Network.HTTP.Lucu.Response
+import Network.HTTP.Lucu.Utils
+#if defined(HAVE_SSL)
 import OpenSSL.X509
+#endif
+import Prelude.Unicode
 
 class Typeable i ⇒ Interaction i where
     toInteraction ∷ i → SomeInteraction
@@ -62,6 +68,13 @@ instance Interaction SomeInteraction where
     toInteraction   = id
     fromInteraction = Just
 
+-- |'EndOfInteraction' is an 'Interaction' indicating the end of
+-- (possibly pipelined) requests. The connection has already been
+-- closed so no need to reply anything.
+data EndOfInteraction = EndOfInteraction
+    deriving Typeable
+instance Interaction EndOfInteraction
+
 -- |'SyntacticallyInvalidInteraction' is an 'Interaction' without even
 -- a syntactically valid 'Request'. The response code will always be
 -- 'BadRequest'.
@@ -75,13 +88,13 @@ instance Interaction SyntacticallyInvalidInteraction
 
 mkSyntacticallyInvalidInteraction ∷ Config
                                   → IO SyntacticallyInvalidInteraction
-mkSyntacticallyInvalidInteraction config@(Config {..})
+mkSyntacticallyInvalidInteraction conf@(Config {..})
     = do date ← getCurrentDate
          let res  = setHeader "Server"       cnfServerSoftware      $
                     setHeader "Date"         date                   $
                     setHeader "Content-Type" defaultPageContentType $
                     emptyResponse BadRequest
-             body = getDefaultPage config Nothing res
+             body = defaultPageForResponse conf Nothing res
          return SYI {
                   syiResponse   = res
                 , syiBodyToSend = body
@@ -94,11 +107,10 @@ data SemanticallyInvalidInteraction
     = SEI {
         seiRequest          ∷ !Request
       , seiExpectedContinue ∷ !Bool
-      , seiReqBodyLength    ∷ !(S.Maybe RequestBodyLength)
+      , seiReqBodyLength    ∷ !(Maybe RequestBodyLength)
 
       , seiResponse         ∷ !Response
       , seiWillChunkBody    ∷ !Bool
-      , seiWillDiscardBody  ∷ !Bool
       , seiWillClose        ∷ !Bool
       , seiBodyToSend       ∷ !Builder
       }
@@ -110,12 +122,19 @@ mkSemanticallyInvalidInteraction ∷ Config
                                  → IO SemanticallyInvalidInteraction
 mkSemanticallyInvalidInteraction config@(Config {..}) (AugmentedRequest {..})
     = do date ← getCurrentDate
-         -- FIXME: DRY
          let res  = setHeader "Server"       cnfServerSoftware      $
                     setHeader "Date"         date                   $
                     setHeader "Content-Type" defaultPageContentType $
+                    ( if arWillChunkBody
+                      then setHeader "Transfer-Encoding" "chunked"
+                      else id
+                    ) $
+                    ( if arWillClose
+                      then setHeader "Connection" "close"
+                      else id
+                    ) $
                     emptyResponse arInitialStatus
-             body = getDefaultPage config (Just arRequest) res
+             body = defaultPageForResponse config (Just arRequest) res
          return SEI {
                   seiRequest          = arRequest
                 , seiExpectedContinue = arExpectedContinue
@@ -123,7 +142,6 @@ mkSemanticallyInvalidInteraction config@(Config {..}) (AugmentedRequest {..})
 
                 , seiResponse         = res
                 , seiWillChunkBody    = arWillChunkBody
-                , seiWillDiscardBody  = arWillDiscardBody
                 , seiWillClose        = arWillClose
                 , seiBodyToSend       = body
                 }
@@ -134,21 +152,23 @@ data NormalInteraction
     = NI {
         niConfig           ∷ !Config
       , niRemoteAddr       ∷ !SockAddr
+#if defined(HAVE_SSL)
       , niRemoteCert       ∷ !(Maybe X509)
+#endif
       , niRequest          ∷ !Request
-      , niResourcePath     ∷ ![Strict.ByteString]
+      , niResourcePath     ∷ !Path
       , niExpectedContinue ∷ !Bool
-      , niReqBodyLength    ∷ !(S.Maybe RequestBodyLength)
+      , niReqBodyLength    ∷ !(Maybe RequestBodyLength)
 
       , niReceiveBodyReq   ∷ !(TMVar ReceiveBodyRequest)
-      , niReceivedBody     ∷ !(TMVar Strict.ByteString)
+      , niReceivedBody     ∷ !(TMVar ByteString)
 
       , niResponse         ∷ !(TVar Response)
       , niSendContinue     ∷ !(TMVar Bool)
       , niWillChunkBody    ∷ !Bool
-      , niWillDiscardBody  ∷ !(TVar Bool)
       , niWillClose        ∷ !(TVar Bool)
       , niResponseHasCType ∷ !(TVar Bool)
+      -- FIXME: use TBChan Builder (in stm-chans package)
       , niBodyToSend       ∷ !(TMVar Builder)
 
       , niState            ∷ !(TVar InteractionState)
@@ -173,17 +193,22 @@ data InteractionState
 
 mkNormalInteraction ∷ Config
                     → SockAddr
+#if defined(HAVE_SSL)
                     → Maybe X509
+#endif
                     → AugmentedRequest
-                    → [Strict.ByteString]
+                    → Path
                     → IO NormalInteraction
+#if defined(HAVE_SSL)
 mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPath
+#else
+mkNormalInteraction config remoteAddr (AugmentedRequest {..}) rsrcPath
+#endif
     = do receiveBodyReq   ← newEmptyTMVarIO
          receivedBody     ← newEmptyTMVarIO
 
          response         ← newTVarIO $ emptyResponse arInitialStatus
          sendContinue     ← newEmptyTMVarIO
-         willDiscardBody  ← newTVarIO arWillDiscardBody
          willClose        ← newTVarIO arWillClose
          responseHasCType ← newTVarIO False
          bodyToSend       ← newEmptyTMVarIO
@@ -193,7 +218,9 @@ mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPat
          return NI {
                   niConfig           = config
                 , niRemoteAddr       = remoteAddr
+#if defined(HAVE_SSL)
                 , niRemoteCert       = remoteCert
+#endif
                 , niRequest          = arRequest
                 , niResourcePath     = rsrcPath
                 , niExpectedContinue = arExpectedContinue
@@ -205,7 +232,6 @@ mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPat
                 , niResponse         = response
                 , niSendContinue     = sendContinue
                 , niWillChunkBody    = arWillChunkBody
-                , niWillDiscardBody  = willDiscardBody
                 , niWillClose        = willClose
                 , niResponseHasCType = responseHasCType
                 , niBodyToSend       = bodyToSend
@@ -216,15 +242,9 @@ mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPat
 type InteractionQueue = TVar (Seq SomeInteraction)
 
 mkInteractionQueue ∷ IO InteractionQueue
+{-# INLINE mkInteractionQueue #-}
 mkInteractionQueue = newTVarIO (∅)
 
-setResponseStatus ∷ NormalInteraction → StatusCode → STM ()
-setResponseStatus (NI {..}) sc
-    = do res ← readTVar niResponse
-         let res' = res {
-                      resStatus = sc
-                    }
-         writeTVar niResponse res'
-
 getCurrentDate ∷ IO Ascii
-getCurrentDate = HTTP.toAscii <$> getCurrentTime
+{-# INLINE getCurrentDate #-}
+getCurrentDate = flip proxy http ∘ cs <$> getCurrentTime