]> gitweb @ CieloNegro.org - Lucu.git/commitdiff
docs
authorPHO <pho@cielonegro.org>
Wed, 30 Nov 2011 15:47:59 +0000 (00:47 +0900)
committerPHO <pho@cielonegro.org>
Wed, 30 Nov 2011 15:47:59 +0000 (00:47 +0900)
Ditz-issue: e0312227f40a0fa92d4c5d69a64dad473f54389a

12 files changed:
Network/HTTP/Lucu/Abortion.hs
Network/HTTP/Lucu/Abortion/Internal.hs
Network/HTTP/Lucu/Authentication.hs
Network/HTTP/Lucu/Config.hs
Network/HTTP/Lucu/Dispatcher/Internal.hs
Network/HTTP/Lucu/Resource.hs
Network/HTTP/Lucu/Resource/Internal.hs
Network/HTTP/Lucu/Response.hs
Network/HTTP/Lucu/StaticFile.hs
Network/HTTP/Lucu/StatusCode/Internal.hs
Network/HTTP/Lucu/Utils.hs
examples/SSL.hs

index 7b69fb53ae95460d6e6d970b47db4094689cbee2..db32c1d95292975a4185d6297407f87f123296fd 100644 (file)
@@ -2,8 +2,8 @@
     FlexibleContexts
   , UnicodeSyntax
   #-}
     FlexibleContexts
   , UnicodeSyntax
   #-}
--- |Aborting the computation of 'Network.HTTP.Lucu.Resource.Resource'
--- in any 'Prelude.IO' monads or arrows.
+-- |Aborting the computation of 'Network.HTTP.Lucu.Rsrc' in any 'IO'
+-- monads.
 module Network.HTTP.Lucu.Abortion
     ( Abortion
     , mkAbortion
 module Network.HTTP.Lucu.Abortion
     ( Abortion
     , mkAbortion
@@ -49,7 +49,7 @@ mkAbortion' sc msg
       }
 
 -- |Throw an 'Abortion' in a 'MonadIO', including the very
       }
 
 -- |Throw an 'Abortion' in a 'MonadIO', including the very
--- 'Network.HTTP.Lucu.Resource.Resource' monad.
+-- 'Network.HTTP.Lucu.Rsrc' monad.
 abort ∷ MonadIO m ⇒ Abortion → m a
 {-# INLINE abort #-}
 abort = liftIO ∘ throwIO
 abort ∷ MonadIO m ⇒ Abortion → m a
 {-# INLINE abort #-}
 abort = liftIO ∘ throwIO
index 573dff01d22eea4fe534def58c10b43300ae52db..69d7a9e9b247ce23a7b725afca47964e70ca616d 100644 (file)
@@ -26,12 +26,12 @@ import Text.XML.HXT.Arrow.XmlArrow
 import Text.XML.HXT.Arrow.XmlState
 
 -- |'Abortion' is an 'Exception' that aborts the execution of
 import Text.XML.HXT.Arrow.XmlState
 
 -- |'Abortion' is an 'Exception' that aborts the execution of
--- 'Network.HTTP.Lucu.Resource.Resource' monad with a 'StatusCode',
--- additional response headers, and an optional message text.
+-- 'Network.HTTP.Lucu.Rsrc' monad with a 'StatusCode', additional
+-- response headers, and an optional message text.
 --
 --
--- 1. If the 'Network.HTTP.Lucu.Resource.Resource' is in the /Deciding
---    Header/ or any precedent states, throwing an 'Abortion' affects
---    the HTTP response to be sent to the client.
+-- 1. If the 'Network.HTTP.Lucu.Rsrc' is in the /Deciding Header/ or
+--    any precedent states, throwing an 'Abortion' affects the HTTP
+--    response to be sent to the client.
 --
 -- 2. Otherwise it's too late to overwrite the HTTP response so the
 --    only possible thing the system can do is to dump the exception
 --
 -- 2. Otherwise it's too late to overwrite the HTTP response so the
 --    only possible thing the system can do is to dump the exception
@@ -41,9 +41,11 @@ import Text.XML.HXT.Arrow.XmlState
 -- 'isError' so you can abuse this exception for redirections as well
 -- as error reporting e.g.
 --
 -- 'isError' so you can abuse this exception for redirections as well
 -- as error reporting e.g.
 --
--- > abort $ mkAbortion MovedPermanently
--- >         [("Location", "http://example.net/")]
--- >         "It has been moved to example.net"
+-- @
+--   'Network.HTTP.Lucu.abort' '$' 'Network.HTTP.Lucu.mkAbortion' 'MovedPermanently'
+--           [(\"Location\", \"http://example.net/\")]
+--           ('Just' \"It's been moved to example.net.\")
+-- @
 data Abortion = Abortion {
       aboStatus  ∷ !SomeStatusCode
     , aboHeaders ∷ !Headers
 data Abortion = Abortion {
       aboStatus  ∷ !SomeStatusCode
     , aboHeaders ∷ !Headers
index 495c931604696c9f8b7a4741bcba861a335254f0..29ae0e92bc1b9752850a7ce8dd342df78fa6203a 100644 (file)
@@ -27,7 +27,7 @@ import Prelude.Unicode
 
 -- |Authentication challenge to be sent to clients with
 -- \"WWW-Authenticate\" header field. See
 
 -- |Authentication challenge to be sent to clients with
 -- \"WWW-Authenticate\" header field. See
--- 'Network.HTTP.Lucu.Resource.setWWWAuthenticate'.
+-- 'Network.HTTP.Lucu.setWWWAuthenticate'.
 data AuthChallenge
     = BasicAuthChallenge !Realm
       deriving (Eq)
 data AuthChallenge
     = BasicAuthChallenge !Realm
       deriving (Eq)
@@ -36,8 +36,7 @@ data AuthChallenge
 type Realm = Ascii
 
 -- |Authorization credential to be sent by client with
 type Realm = Ascii
 
 -- |Authorization credential to be sent by client with
--- \"Authorization\" header. See
--- 'Network.HTTP.Lucu.Resource.getAuthorization'.
+-- \"Authorization\" header. See 'Network.HTTP.Lucu.getAuthorization'.
 data AuthCredential
     = BasicAuthCredential !UserID !Password
       deriving (Show, Eq)
 data AuthCredential
     = BasicAuthCredential !UserID !Password
       deriving (Show, Eq)
index 80845bd21edfdf1f618b9b5f2749dc64c9405a5d..7a2d81f96586972b1cdc3bbd3969576a8ae92d10 100644 (file)
@@ -120,3 +120,4 @@ defaultConfig = Config {
                 , cnfDumpTooLateAbortionToStderr = True
                 , cnfExtToMIMEType               = defaultExtensionMap
                 }
                 , cnfDumpTooLateAbortionToStderr = True
                 , cnfExtToMIMEType               = defaultExtensionMap
                 }
+-- FIXME: use data-default.
index 80effa786e30d4c6a6d5c98b52a9b127b01cd0ec..6e088df8061178f32ffd492187502d4206def0cf 100644 (file)
@@ -79,8 +79,8 @@ class ResourceMapper α where
 -- |Container type for the 'ResourceMapper' type class.
 data ResourceMap = ∀α. ResourceMapper α ⇒ RMap α
 
 -- |Container type for the 'ResourceMapper' type class.
 data ResourceMap = ∀α. ResourceMapper α ⇒ RMap α
 
--- |'ResourceTree' is an opaque structure which is a map from resource
--- path to 'Resource'.
+-- |'ResourceTree' is an opaque structure which a map from resource
+-- 'Path' to 'ResourceNode'.
 --
 -- @
 --   'fromList' [ ([]        , 'nonGreedy' '$' 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/stdio.h\" ) -- \/
 --
 -- @
 --   'fromList' [ ([]        , 'nonGreedy' '$' 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/stdio.h\" ) -- \/
@@ -96,7 +96,8 @@ data ResourceMap = ∀α. ResourceMapper α ⇒ RMap α
 newtype ResourceTree = Tree (M.Map Path ResourceNode)
     deriving Monoid
 
 newtype ResourceTree = Tree (M.Map Path ResourceNode)
     deriving Monoid
 
--- |FIXME: doc
+-- |A node of 'Resource' located somewhere in a 'ResourceTree'. Such
+-- nodes are either 'greedy' or 'nonGreedy'.
 data ResourceNode
     = Greedy    { nResource ∷ !Resource }
     | NonGreedy { nResource ∷ !Resource }
 data ResourceNode
     = Greedy    { nResource ∷ !Resource }
     | NonGreedy { nResource ∷ !Resource }
@@ -106,12 +107,13 @@ data ResourceNode
 -- 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
 -- 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.
+-- fallback. Greedy resource nodes are searched in depth-first
+-- order, just like CGI scripts.
 greedy ∷ Resource → ResourceNode
 {-# INLINE CONLIKE greedy #-}
 greedy = Greedy
 
 greedy ∷ Resource → ResourceNode
 {-# INLINE CONLIKE greedy #-}
 greedy = Greedy
 
--- |FIXME: doc
+-- |Make a normal, non-greedy resource node.
 nonGreedy ∷ Resource → ResourceNode
 {-# INLINE CONLIKE nonGreedy #-}
 nonGreedy = NonGreedy
 nonGreedy ∷ Resource → ResourceNode
 {-# INLINE CONLIKE nonGreedy #-}
 nonGreedy = NonGreedy
index 0ff5081a63dbda5c759fa18a00fb9685fdaf730c..652c5f7b6865d819738287288527a956e08f4332 100644 (file)
@@ -212,9 +212,8 @@ getRequestVersion ∷ Rsrc HttpVersion
 getRequestVersion = reqVersion <$> getRequest
 
 -- |This is an analogy of CGI PATH_INFO. 'getPathInfo' always returns
 getRequestVersion = reqVersion <$> getRequest
 
 -- |This is an analogy of CGI PATH_INFO. 'getPathInfo' always returns
--- @[]@ if the corresponding
--- 'Network.HTTP.Lucu.Resource.Tree.Resource' is not greedy. See:
--- 'getResourcePath'
+-- @[]@ if the corresponding 'Resource' is not greedy. See
+-- 'getResourcePath'.
 --
 -- Note that the returned path components are URI-decoded.
 getPathInfo ∷ Rsrc [Strict.ByteString]
 --
 -- Note that the returned path components are URI-decoded.
 getPathInfo ∷ Rsrc [Strict.ByteString]
@@ -667,7 +666,7 @@ setWWWAuthenticate = setHeader "WWW-Authenticate" ∘ printAuthChallenge
 
 -- |Write a chunk in 'Strict.ByteString' to the response body. You
 -- must first declare the response header \"Content-Type\" before
 
 -- |Write a chunk in 'Strict.ByteString' to the response body. You
 -- must first declare the response header \"Content-Type\" before
--- applying this function. See: 'setContentType'
+-- applying this function. See 'setContentType'.
 putChunk ∷ Strict.ByteString → Rsrc ()
 putChunk = putBuilder ∘ BB.fromByteString
 
 putChunk ∷ Strict.ByteString → Rsrc ()
 putChunk = putBuilder ∘ BB.fromByteString
 
@@ -675,7 +674,7 @@ putChunk = putBuilder ∘ BB.fromByteString
 -- can be safely applied to an infinitely long 'Lazy.ByteString'.
 --
 -- Note that you must first declare the response header
 -- can be safely applied to an infinitely long 'Lazy.ByteString'.
 --
 -- Note that you must first declare the response header
--- \"Content-Type\" before applying this function. See:
--- 'setContentType'
+-- \"Content-Type\" before applying this function. See
+-- 'setContentType'.
 putChunks ∷ Lazy.ByteString → Rsrc ()
 putChunks = putBuilder ∘ BB.fromLazyByteString
 putChunks ∷ Lazy.ByteString → Rsrc ()
 putChunks = putBuilder ∘ BB.fromLazyByteString
index f6d17b6b2f367e32d4956cdccd68431165414db2..f5b54a94d35f0a74a0efacc6ad7a7d6f6bc4f93d 100644 (file)
@@ -261,25 +261,30 @@ getRequest ∷ Rsrc Request
 getRequest = niRequest <$> getInteraction
 
 -- |Get the path of this 'Rsrc' (to be exact, 'Resource') in the
 getRequest = niRequest <$> getInteraction
 
 -- |Get the path of this 'Rsrc' (to be exact, 'Resource') in the
--- 'Network.HTTP.Lucu.Resource.Tree.ResTree'. The result of this
+-- corresponding 'Network.HTTP.Lucu.ResourceTree'. The result of this
 -- action is the exact path in the tree even when the 'Resource' is
 -- action is the exact path in the tree even when the 'Resource' is
--- greedy.
+-- 'Network.HTTP.Lucu.greedy'.
 --
 -- Example:
 --
 --
 -- Example:
 --
--- > main = let tree = mkResTree [ (["foo"], resFoo) ]
--- >        in runHttpd defaultConfig tree []
--- >
--- > resFoo = emptyResource {
--- >     resIsGreedy = True
--- >   , resGet = Just $ do requestURI   <- getRequestURI
--- >                        resourcePath <- getResourcePath
--- >                        pathInfo     <- getPathInfo
--- >                        -- uriPath requestURI == "/foo/bar/baz"
--- >                        -- resourcePath       == ["foo"]
--- >                        -- pathInfo           == ["bar", "baz"]
--- >                        ...
--- >   }
+-- @
+--   main :: 'IO' ()
+--   main = let tree :: 'Network.HTTP.Lucu.ResourceTree'
+--              tree = 'fromList' [ (["foo"], 'Network.HTTP.Lucu.greedy' resFoo) ]
+--          in 'Network.HTTP.Lucu.runHttpd' 'defaultConfig' $ 'Network.HTTP.Lucu.resourceMap' tree
+--
+--   resFoo :: 'Resource'
+--   resFoo = 'singleton'
+--            ( 'GET'
+--            , do requestURI   <- 'getRequestURI'
+--                 resourcePath <- 'getResourcePath'
+--                 pathInfo     <- 'getPathInfo'
+--                 -- 'Network.URI.uriPath' requestURI '==' \"/foo/bar/baz\"
+--                 -- resourcePath       == ["foo"]
+--                 -- pathInfo           == ["bar", "baz"]
+--                 ...
+--            )
+-- @
 getResourcePath ∷ Rsrc Path
 getResourcePath = niResourcePath <$> getInteraction
 
 getResourcePath ∷ Rsrc Path
 getResourcePath = niResourcePath <$> getInteraction
 
@@ -330,7 +335,7 @@ setStatus sc
 -- |@'setHeader' name value@ declares the value of the response header
 -- @name@ as @value@. Note that this function is not intended to be
 -- used so frequently: there should be specialised functions like
 -- |@'setHeader' name value@ declares the value of the response header
 -- @name@ as @value@. Note that this function is not intended to be
 -- used so frequently: there should be specialised functions like
--- 'setContentType' for every common headers.
+-- 'Network.HTTP.Lucu.setContentType' for every common headers.
 --
 -- Some important headers (especially \"Content-Length\" and
 -- \"Transfer-Encoding\") may be silently dropped or overwritten by
 --
 -- Some important headers (especially \"Content-Length\" and
 -- \"Transfer-Encoding\") may be silently dropped or overwritten by
@@ -375,8 +380,8 @@ deleteHeader name = liftIO ∘ atomically ∘ go =≪ getInteraction
 -- infinitely long stream of octets.
 --
 -- Note that you must first declare the response header
 -- infinitely long stream of octets.
 --
 -- Note that you must first declare the response header
--- \"Content-Type\" before applying this function. See:
--- 'setContentType'
+-- \"Content-Type\" before applying this function. See
+-- 'Network.HTTP.Lucu.setContentType'.
 putBuilder ∷ Builder → Rsrc ()
 putBuilder b = liftIO ∘ atomically ∘ go =≪ getInteraction
     where
 putBuilder ∷ Builder → Rsrc ()
 putBuilder b = liftIO ∘ atomically ∘ go =≪ getInteraction
     where
index 826cc0e0ce5d035e9a5aacd2999f59702366ccd8..c18819f8f8ceb782a7e1938f3121c7a5d887beab 100644 (file)
@@ -92,32 +92,32 @@ printResponse (Response {..})
       A.toAsciiBuilder "\x0D\x0A" ⊕
       printHeaders     resHeaders
 
       A.toAsciiBuilder "\x0D\x0A" ⊕
       printHeaders     resHeaders
 
--- |@'isInformational' sc@ returns 'True' iff @sc < 200@.
+-- |@'isInformational' sc@ returns 'True' iff @sc '<' 200@.
 isInformational ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isInformational #-}
 isInformational = satisfy (< 200)
 
 isInformational ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isInformational #-}
 isInformational = satisfy (< 200)
 
--- |@'isSuccessful' sc@ returns 'True' iff @200 <= sc < 300@.
+-- |@'isSuccessful' sc@ returns 'True' iff @200 '<=' sc '<' 300@.
 isSuccessful ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isSuccessful #-}
 isSuccessful = satisfy (\ n → n ≥ 200 ∧ n < 300)
 
 isSuccessful ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isSuccessful #-}
 isSuccessful = satisfy (\ n → n ≥ 200 ∧ n < 300)
 
--- |@'isRedirection' sc@ returns 'True' iff @300 <= sc < 400@.
+-- |@'isRedirection' sc@ returns 'True' iff @300 '<=' sc '<' 400@.
 isRedirection ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isRedirection #-}
 isRedirection = satisfy (\ n → n ≥ 300 ∧ n < 400)
 
 isRedirection ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isRedirection #-}
 isRedirection = satisfy (\ n → n ≥ 300 ∧ n < 400)
 
--- |@'isError' sc@ returns 'True' iff @400 <= sc@
+-- |@'isError' sc@ returns 'True' iff @400 '<=' sc@
 isError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isError #-}
 isError = satisfy (≥ 400)
 
 isError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isError #-}
 isError = satisfy (≥ 400)
 
--- |@'isClientError' sc@ returns 'True' iff @400 <= sc < 500@.
+-- |@'isClientError' sc@ returns 'True' iff @400 '<=' sc '<' 500@.
 isClientError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isClientError #-}
 isClientError = satisfy (\ n → n ≥ 400 ∧ n < 500)
 
 isClientError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isClientError #-}
 isClientError = satisfy (\ n → n ≥ 400 ∧ n < 500)
 
--- |@'isServerError' sc@ returns 'True' iff @500 <= sc@.
+-- |@'isServerError' sc@ returns 'True' iff @500 '<=' sc@.
 isServerError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isServerError #-}
 isServerError = satisfy (≥ 500)
 isServerError ∷ StatusCode sc ⇒ sc → Bool
 {-# INLINE isServerError #-}
 isServerError = satisfy (≥ 500)
index 5ed214aae58dc5b2cea55d78625f1fc7d3b3b4df..a1b611506f557f8893a27155b992158c78ad5b9e 100644 (file)
@@ -72,7 +72,9 @@ handleStaticFile sendContent path
              $ liftIO (LBS.readFile path) ≫= putChunks
 
 -- | @'staticDir' dir@ is a 'Resource' which maps all files in @dir@
              $ liftIO (LBS.readFile path) ≫= putChunks
 
 -- | @'staticDir' dir@ is a 'Resource' which maps all files in @dir@
--- and its subdirectories on the filesystem to the resource tree.
+-- and its subdirectories on the filesystem to the resource tree. Thus
+-- having 'Network.HTTP.Lucu.nonGreedy' 'staticDir' in a tree makes no
+-- sense.
 --
 -- Note that 'staticDir' currently doesn't have a directory-listing
 -- capability. Requesting the content of a directory will end up being
 --
 -- Note that 'staticDir' currently doesn't have a directory-listing
 -- capability. Requesting the content of a directory will end up being
index 24988eefb2707d4e94aede4ade56f8e1a937d177..d6e892b52259156bb7bcdf591b2983a9e7023a53 100644 (file)
@@ -53,8 +53,8 @@ instance Eq SomeStatusCode where
     (SomeStatusCode α) == (SomeStatusCode β) = α ≈ β
 
 infix 4 ≈, ≉
     (SomeStatusCode α) == (SomeStatusCode β) = α ≈ β
 
 infix 4 ≈, ≉
--- |Two 'StatusCode's @a@ and @b@ are said to be equivalent iff
--- @'numericCode' a '==' 'numericCode' b@.
+-- |Equivalence of 'StatusCode's. Two 'StatusCode's @a@ and @b@ are
+-- said to be equivalent iff @'numericCode' a '==' 'numericCode' b@.
 --
 -- U+2248, ALMOST EQUAL TO
 (≈) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
 --
 -- U+2248, ALMOST EQUAL TO
 (≈) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
index 7623c70e45d7d604beb019aa20f09bfe57987a28..9abaf1e9c511d3e0d2d61bf1008640471f2baa51 100644 (file)
@@ -137,21 +137,21 @@ trim = reverse ∘ f ∘ reverse ∘ f
       f = dropWhile isSpace
 
 infixr 5 ⊲
       f = dropWhile isSpace
 
 infixr 5 ⊲
--- | (&#22B2;) = ('<|')
+-- | (&#x22B2;) = ('<|')
 --
 -- U+22B2, NORMAL SUBGROUP OF
 (⊲) ∷ Sequence α a ⇒ a → α → α
 (⊲) = (<|)
 
 infixl 5 ⊳
 --
 -- U+22B2, NORMAL SUBGROUP OF
 (⊲) ∷ Sequence α a ⇒ a → α → α
 (⊲) = (<|)
 
 infixl 5 ⊳
--- | (&#22B3;) = ('|>')
+-- | (&#x22B3;) = ('|>')
 --
 -- U+22B3, CONTAINS AS NORMAL SUBGROUP
 (⊳) ∷ Sequence α a ⇒ α → a → α
 (⊳) = (|>)
 
 infixr 5 ⋈
 --
 -- U+22B3, CONTAINS AS NORMAL SUBGROUP
 (⊳) ∷ Sequence α a ⇒ α → a → α
 (⊳) = (|>)
 
 infixr 5 ⋈
--- | (&#22C8;) = ('><')
+-- | (&#x22C8;) = ('><')
 --
 -- U+22C8, BOWTIE
 (⋈) ∷ Sequence α a ⇒ α → α → α
 --
 -- U+22C8, BOWTIE
 (⋈) ∷ Sequence α a ⇒ α → α → α
index 624ed042647729a3003849c4106d7ef293fc63b1..aa8b3a596ffa29031eb136d5b7587976c4ac3f0e 100644 (file)
@@ -42,17 +42,16 @@ main = withOpenSSL $
 
 helloWorld ∷ Resource
 helloWorld 
 
 helloWorld ∷ Resource
 helloWorld 
-    = C.fromList
-      [ ( GET
-        , do setContentType [mimeType| text/plain |]
-             putChunk "getRemoteCertificate = "
-             cert ← do cert ← getRemoteCertificate
-                       case cert of
-                         Just c  → liftIO $ Lazy.pack <$> printX509 c
-                         Nothing → return "Nothing"
-             putChunks cert
-        )
-      ]
+    = C.singleton
+      ( GET
+      , do setContentType [mimeType| text/plain |]
+           putChunk "getRemoteCertificate = "
+           cert ← do cert ← getRemoteCertificate
+                     case cert of
+                       Just c  → liftIO $ Lazy.pack <$> printX509 c
+                       Nothing → return "Nothing"
+           putChunks cert
+      )
 
 genCert ∷ KeyPair k ⇒ k → IO X509
 genCert pkey
 
 genCert ∷ KeyPair k ⇒ k → IO X509
 genCert pkey