implemented runConnection logic

This commit is contained in:
Jonathan Lamothe 2021-11-10 15:04:00 -05:00
parent 17f1eba583
commit e47bc3ce48
5 changed files with 74 additions and 20 deletions

View File

@ -45,6 +45,7 @@ test-suite gemserv-test
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
main-is: Spec.hs main-is: Spec.hs
other-modules: other-modules:
Network.GemServ.InternalSpec
Network.GemServSpec Network.GemServSpec
Paths_gemserv Paths_gemserv
hs-source-dirs: hs-source-dirs:

View File

@ -42,12 +42,13 @@ module Network.GemServ (
import Control.Concurrent (forkIO) import Control.Concurrent (forkIO)
import Control.Exception (IOException, try) import Control.Exception (IOException, try)
import Control.Exception.Base (bracket) import Control.Exception.Base (bracket, finally)
import Control.Monad (void) import Control.Monad (void)
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.ByteString.Builder (charUtf8, stringUtf8, toLazyByteString) import Data.ByteString.Builder (charUtf8, stringUtf8, toLazyByteString)
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
import Data.Char (chr, ord, toLower) import Data.Char (chr, ord, toLower)
import qualified Data.Connection as C
import Data.IORef (IORef, newIORef, readIORef, writeIORef) import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.List (find, intercalate) import Data.List (find, intercalate)
import Data.Maybe (fromJust) import Data.Maybe (fromJust)
@ -55,10 +56,10 @@ import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8') import Data.Text.Encoding (decodeUtf8')
import Data.TLSSetting (makeServerParams) import Data.TLSSetting (makeServerParams)
import Data.X509 (Certificate, CertificateChain (..), getSigned, signedObject) import Data.X509 (Certificate, CertificateChain (..), getSigned, signedObject)
import Network.Socket (Socket, close) import qualified Network.Socket as S
import Network.TLS (ServerParams, onClientCertificate, serverHooks) import Network.TLS (ServerParams, onClientCertificate, serverHooks)
import System.IO.Streams.TCP (bindAndListen) import System.IO.Streams.TCP (bindAndListen)
import System.IO.Streams.TLS (accept) import System.IO.Streams.TLS (TLSConnection, accept)
import Network.GemServ.Internal import Network.GemServ.Internal
import Network.GemServ.Types import Network.GemServ.Types
@ -75,7 +76,7 @@ runGemServer settings handler = bracket
(servConnections settings) (servConnections settings)
(fromIntegral $ servPort settings) (fromIntegral $ servPort settings)
) )
close S.close
( \sock -> do ( \sock -> do
params <- makeServerParams params <- makeServerParams
(servCert settings) (servCert settings)
@ -177,14 +178,15 @@ unescapeString str = case decodeUtf8' $ BS.pack $ toBytes str of
toNum ch = fst $ fromJust $ toNum ch = fst $ fromJust $
find (\x -> snd x == ch) $ zip [0..] hexDigits find (\x -> snd x == ch) $ zip [0..] hexDigits
listenLoop :: Socket -> ServerParams -> GemHandler -> IO a listenLoop :: S.Socket -> ServerParams -> GemHandler -> IO a
listenLoop sock params handler = do listenLoop sock params handler = do
certRef <- newIORef Nothing certRef <- newIORef Nothing
let params' = adjustServerParams certRef params let params' = adjustServerParams certRef params
try (accept params' sock) >>= \case try (accept params' sock) >>= \case
Left (_ :: IOException) -> return () Left (_ :: IOException) -> return ()
Right conn -> void $ forkIO $ Right conn -> void $ forkIO $ finally
readIORef certRef >>= runConnection conn handler (readIORef certRef >>= runConnection conn handler)
(C.close conn)
listenLoop sock params handler listenLoop sock params handler
adjustServerParams adjustServerParams
@ -205,6 +207,20 @@ adjustServerParams certRef params = let
hooks' = hooks { onClientCertificate = certHook' } hooks' = hooks { onClientCertificate = certHook' }
in params { serverHooks = hooks' } in params { serverHooks = hooks' }
runConnection
:: TLSConnection
-> GemHandler
-> Maybe Certificate
-> IO ()
runConnection conn handler mCert =
( readURL conn >>= \case
Nothing -> return $ newGemResponse
{ respStatus = 59
, respMeta = "bad request"
}
Just url -> handler (newGemRequest url) { reqCert = mCert }
) >>= sendResponse conn
hexDigits :: String hexDigits :: String
hexDigits = ['0'..'9'] ++ ['a'..'f'] hexDigits = ['0'..'9'] ++ ['a'..'f']

View File

@ -31,24 +31,28 @@ time.
-} -}
module Network.GemServ.Internal ( module Network.GemServ.Internal (
runConnection readURL,
sendResponse
) where ) where
import Data.X509 (Certificate) import Data.Connection (Connection)
import System.IO.Streams.TLS (TLSConnection)
import Network.GemServ.Types import Network.GemServ.Types
-- | takes a Gemini request from a 'TLSConnection' and replies with -- | Reads a 'GemURL' from a 'Connection'
-- the response readURL
runConnection :: Connection a
:: TLSConnection -- ^ the connection
-- ^ The connection -> IO (Maybe GemURL)
-> GemHandler readURL = undefined
-- ^ The handler that produces the response
-> Maybe Certificate -- | Sends a 'GemResponse' to a 'Connection'
-- ^ The client certificate (if available) sendResponse
:: Connection a
-- ^ the connection
-> GemResponse
-- ^ the response being sent
-> IO () -> IO ()
runConnection = undefined sendResponse = undefined
--jl --jl

View File

@ -0,0 +1,30 @@
{-
gemserv
Cooyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with this program. If not, see
<https://www.gnu.org/licenses/>.
-}
module Network.GemServ.InternalSpec (spec) where
import Test.Hspec (Spec, describe)
spec :: Spec
spec = describe "Internal" $ return ()
--jl

View File

@ -24,11 +24,14 @@ module Network.GemServSpec (spec) where
import Test.Hspec (Spec, context, describe, it, shouldBe) import Test.Hspec (Spec, context, describe, it, shouldBe)
import qualified Network.GemServ.InternalSpec as Internal
import Network.GemServ import Network.GemServ
import Network.GemServ.Types import Network.GemServ.Types
spec :: Spec spec :: Spec
spec = describe "Network.GemServ" $ do spec = describe "Network.GemServ" $ do
Internal.spec
encodeGemURLSpec encodeGemURLSpec
decodeGemURLSpec decodeGemURLSpec
escapeStringSpec escapeStringSpec