0 | module WebServerRacket
9 | import Http2Responder
11 | import System as System
12 | import System.Concurrency as Concurrency
13 | import Data.Buffer as Buffer
14 | import Data.Buffer.Core as BufCore
15 | import Data.List as List
16 | import Data.Maybe as Maybe
21 | setBytes : Buffer -> Int -> List Bits8 -> IO Unit
22 | setBytes buf offset [] = pure ()
23 | setBytes buf offset (x :: xs) = do
24 | Buffer.setBits8 buf offset x
25 | setBytes buf (offset + 1) xs
29 | pgMutex : Concurrency.Mutex
30 | pgInputChan : Channel PgInput
31 | pgOutputChan : Channel PgRows
33 | SendRecvIterator : Type
34 | SendRecvIterator = DIterator Response Act PgInput PgRows PgErr
36 | natToInt : Nat -> Int
39 | natToBits64 : Nat -> Bits64
42 | intToBits8 : Int -> Bits8
45 | newBuf : Nat -> IO Buffer
47 | mbBuf <- Buffer.newBuffer (natToInt nat)
53 | assert_total $
idris_crash "Impossible"
54 | Just buf => pure buf
56 | data ContState c d res
60 | | MkUnexpectedAmountWritten
61 | | MkGotCont (d, c -> DIterator Response Act c d res)
63 | runTillNeedsSql : InputPort -> OutputPort -> DIterator Response Act c d res -> IO (ContState c d res)
64 | runTillNeedsSql pgInputPort pgOutputPort oldIter = do
66 | sender : List Bits8 -> IO (Either WriteError Bits64)
67 | sender pgToSend = do
68 | writeBuf <- newBuf (length pgToSend)
69 | setBytes writeBuf 0 pgToSend
70 | writeBytes writeBuf pgOutputPort
72 | Susp (ActSend toSend) needPgInput => do
73 | Right written <- sender toSend
74 | | Left MkWriteError => pure MkGotWriteError
75 | if (written /= natToBits64 (length toSend))
76 | then pure MkUnexpectedAmountWritten
78 | putStrLn $
"Sent " <+> show written <+> " bytes"
79 | runTillNeedsSql pgInputPort pgOutputPort (needPgInput RSent)
80 | Susp ActReceive needPgInput => do
81 | syncEvt <- readBytesEvt 1 pgInputPort
82 | Right readBuf <- readSync syncEvt
83 | | Left MkEOF => pure MkGotEof
84 | readBufData <- map intToBits8 <$> Buffer.bufferData readBuf
85 | let newIter = needPgInput (RReceived readBufData)
87 | runTillNeedsSql pgInputPort pgOutputPort newIter
88 | Susp2 rows needSql =>
89 | pure (MkGotCont (rows, needSql))
91 | pure (MkGotResult err)
93 | client : DIterator Response Act PgRows PgInput Void -> InputPort -> OutputPort -> PgCtx -> IO Unit
94 | client h2Iter inputPort outputPort pgCtx = do
95 | MkGotCont (pgInput, needsRows) <- runTillNeedsSql inputPort outputPort h2Iter
96 | | MkGotWriteError => putStrLn "Got error while writing to HTTP2"
97 | | MkUnexpectedAmountWritten => putStrLn "Got unexpected amount written to HTTP2"
98 | | MkGotEof => putStrLn "Got EOF from HTTP2"
99 | | MkGotResult void => absurd void
100 | Concurrency.mutexAcquire pgCtx.pgMutex
101 | Concurrency.channelPut pgCtx.pgInputChan pgInput
102 | rows <- Concurrency.channelGet pgCtx.pgOutputChan
103 | Concurrency.mutexRelease pgCtx.pgMutex
104 | client (needsRows rows) inputPort outputPort pgCtx
107 | runResponder : PGAuthentication -> (Request -> InnerCont (List Frame)) -> IO Unit
108 | runResponder pgAuth responder = do
109 | pgCtx <- MkPgCtx <$> Concurrency.makeMutex <*> Concurrency.makeChannel <*> Concurrency.makeChannel
111 | Right (pgIn, pgOut) <- tcpConnect "localhost" 5432
112 | | Left MkCouldn'tConnect => putStrLn "Couldn't connect to PostgreSQL at localhost:5432"
114 | MkGotCont ([], needsSql) <- runTillNeedsSql pgIn pgOut (mkSendRecvIterator $
initialPgIter pgAuth)
115 | | MkGotCont (rows, needsSql) => putStrLn "Expected no rows"
116 | | MkGotEof => putStrLn "Err in pg handshake: Got End-Of-File"
117 | | MkGotResult pgErr => putStrLn $
"Err in pg handshake: PgErr: " <+> show pgErr
118 | | MkGotWriteError => putStrLn "Err in pg handshake: WriteError"
119 | | MkUnexpectedAmountWritten => putStrLn "Err in pg handshake: UnexpectedAmountWritten"
120 | putStrLn "Done with PostgreSQL handshake"
122 | pgForever : (PgInput -> SendRecvIterator) -> IO Unit
123 | pgForever iter = do
124 | pgInput <- Concurrency.channelGet pgCtx.pgInputChan
125 | MkGotCont (result, needsSql2) <- runTillNeedsSql pgIn pgOut $
iter pgInput
126 | | MkGotEof => putStrLn "Err in pg sql execution: Got End-Of-File on PostgreSQL connection"
127 | | MkGotResult pgErr => putStrLn $
"Err in pg sql execution: PgErr: " <+> show pgErr
128 | | MkGotWriteError => putStrLn "Err in pg sql execution: WriteError"
129 | | MkUnexpectedAmountWritten => putStrLn "Err in pg sql execution: UnexpectedAmountWritten"
130 | Concurrency.channelPut pgCtx.pgOutputChan result
131 | pgForever needsSql2
133 | pgThreadId <- fork (pgForever needsSql)
135 | Right listener <- tcpListen 8000
136 | | Left MkListenErr => putStrLn "Couldn't listen on port 8000"
137 | putStrLn "Listening on port 8000..."
139 | initialHttpIter: DIterator (List Bits8) (List Bits8) PgRows PgInput Void
140 | initialHttpIter = Http2.mkInitialHttp2Iter responder
143 | (inputPortRaw, outputPortRaw) <- tcpAccept listener
144 | let h2Buf = BufCore.unsafeGetBuffer $
UTF8.utf8Encode "h2"
145 | errOrPorts <- portsToSslPorts inputPortRaw outputPortRaw "key.pem" "cert.pem" h2Buf
147 | Left MkCouldn'tSetUpTLS => do
148 | putStrLn "Couldn't set up TLS for client, dropping..."
150 | Right (inputPort, outputPort) => do
151 | h2ThreadId <- fork $
client (mkSendRecvIterator initialHttpIter) inputPort outputPort pgCtx