]> gitweb @ CieloNegro.org - Lucu.git/commitdiff
HelloWorld works again.
authorPHO <pho@cielonegro.org>
Sat, 26 Nov 2011 04:14:03 +0000 (13:14 +0900)
committerPHO <pho@cielonegro.org>
Sat, 26 Nov 2011 04:14:03 +0000 (13:14 +0900)
Ditz-issue: e0312227f40a0fa92d4c5d69a64dad473f54389a

Network/HTTP/Lucu.hs
Network/HTTP/Lucu/Dispatcher/Internal.hs
Network/HTTP/Lucu/Httpd.hs
Network/HTTP/Lucu/Implant/PrettyPrint.hs
Network/HTTP/Lucu/Interaction.hs
Network/HTTP/Lucu/RequestReader.hs
Network/HTTP/Lucu/Resource.hs
Network/HTTP/Lucu/Resource/Internal.hs
Network/HTTP/Lucu/StaticFile.hs
examples/HelloWorld.hs

index 8ef80d94a51f712b7e30fa6b4f33cd5d369e8ef6..6a827d08412253cf9f5cfc06fab5703b4f6bf2da 100644 (file)
@@ -46,6 +46,9 @@ module Network.HTTP.Lucu
     , module Network.HTTP.Lucu.Resource
 
       -- ** Things to be used in the Resource monad
+      -- *** 'Method'
+    , Method(..)
+
       -- *** 'StatusCode'
     , module Network.HTTP.Lucu.StatusCode
 
@@ -82,6 +85,7 @@ import Network.HTTP.Lucu.Httpd
 import Network.HTTP.Lucu.MIMEParams
 import Network.HTTP.Lucu.MIMEType hiding (mimeType)
 import Network.HTTP.Lucu.MIMEType.TH
+import Network.HTTP.Lucu.Request
 import Network.HTTP.Lucu.Resource
 import Network.HTTP.Lucu.Response
 import Network.HTTP.Lucu.StaticFile
index 72589200892f3f230cdf644082da384cd16a6dae..eea8d4621648088e59f797074d442787e059de6d 100644 (file)
@@ -26,13 +26,14 @@ import Control.Applicative hiding (empty)
 import Control.Monad.Trans.Maybe
 import Control.Monad.Unicode
 import Data.Collections
+import qualified Data.Collections.Newtype.TH as C
 import qualified Data.Map as M
 import Data.Monoid
 import Data.Monoid.Unicode
 import Network.HTTP.Lucu.Resource.Internal
 import Network.HTTP.Lucu.Utils
 import Network.URI hiding (path)
-import Prelude hiding (filter, lookup, null)
+import Prelude hiding (filter, foldr, lookup, null)
 import Prelude.Unicode
 
 -- |FIXME: docs
@@ -81,7 +82,12 @@ data ResourceNode
     = Greedy    { nResource ∷ !Resource }
     | NonGreedy { nResource ∷ !Resource }
 
--- |FIXME: doc
+-- |Make a greedy resource node.
+--
+-- Say a client is trying to access \"\/aaa\/bbb\/ccc\' while there is
+-- no resource node at the path. If there are greedy resource nodes at
+-- \"\/aaa\/bbb\", \"\/aaa\" or \"/\" they will be chosen instead as a
+-- fallback. Greedy resource nodes are searched in depth-first order.
 greedy ∷ Resource → ResourceNode
 {-# INLINE CONLIKE greedy #-}
 greedy = Greedy
@@ -204,6 +210,13 @@ canonPath ∷ (Collection c f, Foldable f e) ⇒ c → c
 {-# INLINEABLE canonPath #-}
 canonPath = filter ((¬) ∘ null)
 
+C.derive [d| instance Foldable ResourceTree (Path, ResourceNode)
+           |]
+
+instance Collection ResourceTree (Path, ResourceNode) where
+    {-# INLINE filter #-}
+    filter f (Tree m) = Tree $ filter f m
+
 -- |'findResource' performs the longest prefix match on the tree,
 -- finding the most specific one.
 instance ResourceMapper ResourceTree where
index fb87d820f13f42a665c1b4a86cc99f6678fdaed4..ac1dc77a66379508ac8f1bf2c0528c1f2ddd037a 100644 (file)
@@ -48,7 +48,7 @@ import Prelude.Unicode
 -- >              }
 --
 -- FIXME: update the above example
-runHttpd ∷ Config → HostMap → IO ()
+runHttpd ∷ HostMapper α ⇒ Config → α → IO ()
 runHttpd cnf hm
     = do let launchers
                  = catMaybes
index c79b4d4b004c5526bd5f605f1c2324389fa3391c..bcb6f0451325e3a0739e1f9ce657c94a8ff5013f 100644 (file)
@@ -20,6 +20,7 @@ import qualified Data.ByteString.Lazy as L
 import Data.Char
 import Data.Collections
 import Data.List (intersperse)
+import Data.Monoid
 import Data.Ratio
 import Data.Time
 import Language.Haskell.TH.Lib
@@ -152,7 +153,7 @@ resourceDecl i symName
                 = []
 
 resourceE ∷ Input → Q Exp
-resourceE i = [| emptyResource {
+resourceE i = [| mempty {
                    resGet  = $(resGetE  i)
                  , resHead = $(resHeadE i)
                  }
@@ -239,6 +240,7 @@ rules = [ qualifyAll   "Codec.Compression.GZip"              "G"
         , unqualifyAll "Network.HTTP.Lucu.MIMEParams"        "Network.HTTP.Lucu"
         , unqualifyAll "Network.HTTP.Lucu.MIMEType"          "Network.HTTP.Lucu"
         , unqualify    'when                                 "Control.Monad"
+        , unqualify    'mempty                               "Data.Monoid"
         , unqualify    '(%)                                  "Data.Ratio"
         , unqualify    ''DiffTime                            "Data.Time"
         , unqualifyIn  'ModifiedJulianDay  ''Day             "Data.Time"
index 0f3e7bfeae5492a73edddd387528d9368dca57e8..f5ccd83826f7d64e27583fb2491c75c329ecebfb 100644 (file)
@@ -123,6 +123,14 @@ mkSemanticallyInvalidInteraction config@(Config {..}) (AugmentedRequest {..})
          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
          return SEI {
index d793703c6c64ff240c8f3aaec1b36bd47be5205e..2cdc45dc472a42e284472badd85df1f31582c9f5 100644 (file)
@@ -53,17 +53,17 @@ data ChunkReceivingState
     | InChunk !Int -- ^Number of remaining octets in the current
                    -- chunk. It's always positive.
 
-requestReader ∷ HandleLike h
+requestReader ∷ (HostMapper hm, HandleLike h)
               ⇒ Config
-              → HostMap
+              → hm
               → h
               → PortNumber
               → SockAddr
               → InteractionQueue
               → IO ()
-requestReader cnf sm h port addr tQueue
+requestReader cnf hm h port addr tQueue
     = do input ← hGetLBS h
-         acceptRequest (Context cnf sm h port addr tQueue) input
+         acceptRequest (Context cnf (hostMap hm) h port addr tQueue) input
       `catches`
       [ Handler handleAsyncE
       , Handler handleOthers
index 64e69fbd6e9a8762145ec91f73e4569253076aa4..0ff5081a63dbda5c759fa18a00fb9685fdaf730c 100644 (file)
@@ -71,7 +71,6 @@ module Network.HTTP.Lucu.Resource
     (
     -- * Types
       Resource(..)
-    , emptyResource
     , Rsrc
     , FormData(..)
 
index 96f6c2af51a43819485264578c80d2baf7d8543a..f6d17b6b2f367e32d4956cdccd68431165414db2 100644 (file)
@@ -1,15 +1,16 @@
 {-# LANGUAGE
     CPP
   , DoAndIfThenElse
+  , FlexibleInstances
   , GeneralizedNewtypeDeriving
   , OverloadedStrings
+  , MultiParamTypeClasses
   , RecordWildCards
   , UnicodeSyntax
   #-}
 module Network.HTTP.Lucu.Resource.Internal
     ( Rsrc
     , Resource(..)
-    , emptyResource
     , spawnRsrc
 
     , getConfig
@@ -64,7 +65,7 @@ import Network.Socket
 #if defined(HAVE_SSL)
 import OpenSSL.X509
 #endif
-import Prelude hiding (catch, concat, mapM_, tail)
+import Prelude hiding (catch, concat, filter, mapM_, tail)
 import Prelude.Unicode
 import System.IO
 
@@ -82,14 +83,6 @@ runRsrc = runReaderT ∘ unRsrc
 -- |'Resource' is basically a set of 'Rsrc' monadic computations for
 -- each HTTP methods.
 data Resource = Resource {
-    -- | Whether to be greedy or not.
-    --
-    -- Say a client is trying to access \/aaa\/bbb\/ccc. If there is a
-    -- greedy resource at \/aaa\/bbb, it is always chosen even if
-    -- there is another resource at \/aaa\/bbb\/ccc. If the resource
-    -- at \/aaa\/bbb is not greedy, it is just ignored. Greedy
-    -- resources are like CGI scripts.
-      resIsGreedy         ∷ !Bool
     -- |A 'Rsrc' to be run when a GET request comes for the
     -- resource path. If 'resGet' is Nothing, the system responds
     -- \"405 Method Not Allowed\" for GET requests.
@@ -97,7 +90,7 @@ data Resource = Resource {
     -- It also runs for HEAD request if the 'resHead' is 'Nothing'. In
     -- that case 'putChunk' and such don't actually write a response
     -- body.
-    , resGet              ∷ !(Maybe (Rsrc ()))
+      resGet              ∷ !(Maybe (Rsrc ()))
     -- |A 'Rsrc' to be run when a HEAD request comes for the
     -- resource path. If 'resHead' is Nothing, the system runs
     -- 'resGet' instead. If 'resGet' is also Nothing, the system
@@ -117,30 +110,49 @@ data Resource = Resource {
     , resDelete           ∷ !(Maybe (Rsrc ()))
     }
 
--- |'emptyResource' is a resource definition with no actual
--- handlers. You can construct a 'Resource' by selectively overriding
--- 'emptyResource'. It is defined as follows:
---
--- @
---   emptyResource = Resource {
---                     resUsesNativeThread = False
---                   , resIsGreedy         = False
---                   , resGet              = Nothing
---                   , resHead             = Nothing
---                   , resPost             = Nothing
---                   , resPut              = Nothing
---                   , resDelete           = Nothing
---                   }
--- @
-emptyResource ∷ Resource
-emptyResource = Resource {
-                  resIsGreedy         = False
-                , resGet              = Nothing
-                , resHead             = Nothing
-                , resPost             = Nothing
-                , resPut              = Nothing
-                , resDelete           = Nothing
-                }
+instance Monoid Resource where
+    {-# INLINE mempty #-}
+    mempty
+        = Resource {
+            resGet    = Nothing
+          , resHead   = Nothing
+          , resPost   = Nothing
+          , resPut    = Nothing
+          , resDelete = Nothing
+          }
+    {-# INLINEABLE mappend #-}
+    mappend a b
+        = Resource {
+            resGet    = resGet    a <|> resGet    b
+          , resHead   = resHead   a <|> resHead   b
+          , resPost   = resPost   a <|> resPost   b
+          , resPut    = resPut    a <|> resPut    b
+          , resDelete = resDelete a <|> resDelete b
+          }
+
+instance Unfoldable Resource (Method, Rsrc ()) where
+    {-# INLINEABLE insert #-}
+    insert (GET   , a) r = r { resGet    = Just a }
+    insert (HEAD  , a) r = r { resHead   = Just a }
+    insert (POST  , a) r = r { resPost   = Just a }
+    insert (PUT   , a) r = r { resPut    = Just a }
+    insert (DELETE, a) r = r { resDelete = Just a }
+    insert _           r = r
+    {-# INLINE empty #-}
+    empty = (∅)
+
+instance Foldable Resource (Method, Rsrc ()) where
+    {-# INLINEABLE foldMap #-}
+    foldMap f (Resource {..})
+        = maybe (∅) (f ∘ ((,) GET   )) resGet  ⊕
+          maybe (∅) (f ∘ ((,) HEAD  )) resHead ⊕
+          maybe (∅) (f ∘ ((,) POST  )) resPost ⊕
+          maybe (∅) (f ∘ ((,) PUT   )) resPut  ⊕
+          maybe (∅) (f ∘ ((,) DELETE)) resDelete
+
+instance Collection Resource (Method, Rsrc ()) where
+    {-# INLINE filter #-}
+    filter = (fromList ∘) ∘ (∘ fromFoldable) ∘ filter
 
 spawnRsrc ∷ Resource → NormalInteraction → IO ThreadId
 spawnRsrc (Resource {..}) ni@(NI {..})
index 5b5eb9734e3a68441516f36a86ada99269ea7888..5ed214aae58dc5b2cea55d78625f1fc7d3b3b4df 100644 (file)
@@ -15,6 +15,7 @@ import Control.Monad.Unicode
 import Control.Monad.Trans
 import Data.ByteString (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as LBS
+import Data.Monoid.Unicode
 import Data.String
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
@@ -35,7 +36,7 @@ import System.FilePath
 -- @fpath@ on the filesystem.
 staticFile ∷ FilePath → Resource
 staticFile path
-    = emptyResource {
+    = (∅) {
         resGet  = Just $ handleStaticFile True  path
       , resHead = Just $ handleStaticFile False path
       }
@@ -71,18 +72,16 @@ handleStaticFile sendContent path
              $ liftIO (LBS.readFile path) ≫= putChunks
 
 -- | @'staticDir' dir@ is a 'Resource' which maps all files in @dir@
--- and its subdirectories on the filesystem to the
--- 'Network.HTTP.Lucu.Resource.Tree.ResTree'.
+-- and its subdirectories on the filesystem to the resource tree.
 --
 -- Note that 'staticDir' currently doesn't have a directory-listing
 -- capability. Requesting the content of a directory will end up being
 -- replied with /403 Forbidden/.
 staticDir ∷ FilePath → Resource
 staticDir path
-    = emptyResource {
-        resIsGreedy = True
-      , resGet      = Just $ handleStaticDir True  path
-      , resHead     = Just $ handleStaticDir False path
+    = (∅) {
+        resGet  = Just $ handleStaticDir True  path
+      , resHead = Just $ handleStaticDir False path
       }
 
 -- TODO: implement directory listing.
index f8a4721dca6c5503a31e50715e69e43d0c0c2273..6a732565a78b8c4cba35020df1eda9a8e8fb6d52 100644 (file)
@@ -6,35 +6,42 @@
 import Control.Applicative
 import Control.Monad.Unicode
 import qualified Data.ByteString.Lazy.Char8 as Lazy
+import qualified Data.Collections as C
+import Data.Monoid.Unicode
 import Network.HTTP.Lucu
+import Prelude.Unicode
 
 main ∷ IO ()
 main = let config    = defaultConfig { cnfServerPort = "9999" }
-           resources = mkResTree
-                       [ ([]         , helloWorld               )
-                       , (["urandom"], staticFile "/dev/urandom")
-                       , (["inc"    ], staticDir "/usr/include" )
-                       ]
-           fallbacks = [ \ path → case path of
-                                     ["hello"] → return $ Just helloWorld
-                                     _         → return Nothing
+           mapper    = resourceMap resources ⊕ resourceMap fallbacks
+           resources ∷ ResourceTree
+           resources = C.fromList
+                       [ ([]          , nonGreedy helloWorld)
+                       , (["urandom" ], nonGreedy $ staticFile "/dev/urandom")
+                       , (["inc"     ], greedy    $ staticDir  "/usr/include")
+                       , (["inc", "t"], nonGreedy $ staticFile "/usr/include/time.h")
                        ]
+           fallbacks ∷ Path → Maybe (Path, Resource)
+           fallbacks path
+               | path ≡ ["hello"] = Just (path, helloWorld)
+               | otherwise        = Nothing
        in
          do putStrLn "Access http://localhost:9999/ with your browser."
-            runHttpd config resources fallbacks
+            runHttpd config mapper
 
-helloWorld ∷ ResourceDef
-helloWorld
-    = emptyResource {
-        resGet
-          = Just $ do setContentType [mimeType| text/hello |]
-                      putChunk "Hello, "
-                      putChunk "World!\n"
-                      putChunks =≪ Lazy.pack <$> getRemoteAddr'
-      , resPost
-          = Just $ do str1 ← getChunk 3
-                      str2 ← getChunk 3
-                      str3 ← getChunk 3
-                      setContentType [mimeType| text/hello |]
-                      putChunks $ Lazy.fromChunks ["[", str1, " - ", str2, "#", str3, "]"]
-      }
+helloWorld ∷ Resource
+helloWorld = C.fromList
+             [ ( GET
+               , do setContentType [mimeType| text/hello |]
+                    putChunk "Hello, "
+                    putChunk "World!\n"
+                    putChunks =≪ Lazy.pack <$> getRemoteAddr'
+               )
+             , ( POST
+               , do str1 ← getChunk 3
+                    str2 ← getChunk 3
+                    str3 ← getChunk 3
+                    setContentType [mimeType| text/hello |]
+                    putChunks $ Lazy.fromChunks ["[", str1, " - ", str2, "#", str3, "]"]
+               )
+             ]