2020-10-02 18:10:32 +03:00
|
|
|
{-# LANGUAGE StrictData #-}
|
|
|
|
|
2020-10-27 15:04:31 +03:00
|
|
|
-- This is required due to the use of 'Void' in a constructor slot in
|
|
|
|
-- combination with 'deriveNoun' which generates an unreachable pattern.
|
|
|
|
{-# OPTIONS_GHC -Wno-overlapping-patterns #-}
|
|
|
|
|
2020-11-03 22:29:51 +03:00
|
|
|
-- Hack. See comment above instance ToNoun H.StdMethod
|
|
|
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
|
|
|
Types used in both Events and Effects.
|
|
|
|
-}
|
2020-01-24 08:28:38 +03:00
|
|
|
module Urbit.Arvo.Common
|
2019-08-03 03:09:53 +03:00
|
|
|
( KingId(..), ServId(..)
|
2020-12-03 21:41:03 +03:00
|
|
|
, Vere(..), Wynn(..)
|
2019-07-24 04:34:16 +03:00
|
|
|
, Json, JsonNode(..)
|
|
|
|
, Desk(..), Mime(..)
|
2019-12-10 05:45:19 +03:00
|
|
|
, Port(..), Turf(..)
|
2019-08-02 09:56:42 +03:00
|
|
|
, HttpServerConf(..), PEM(..), Key, Cert
|
2019-08-03 03:09:53 +03:00
|
|
|
, HttpEvent(..), Method, Header(..), ResponseHeader(..)
|
2019-07-24 07:03:04 +03:00
|
|
|
, ReOrg(..), reorgThroughNoun
|
2020-11-03 22:03:58 +03:00
|
|
|
, AmesDest, Ipv4(..), Ipv6(..), Patp(..), Galaxy, AmesAddress(..)
|
2019-07-24 04:34:16 +03:00
|
|
|
) where
|
|
|
|
|
2020-12-03 21:41:03 +03:00
|
|
|
import Urbit.Prelude
|
2019-07-24 04:34:16 +03:00
|
|
|
|
2020-10-27 15:22:33 +03:00
|
|
|
import Control.Monad.Fail (fail)
|
2021-02-02 21:54:07 +03:00
|
|
|
import Data.Serialize
|
2020-10-27 15:22:33 +03:00
|
|
|
|
2019-07-24 04:34:16 +03:00
|
|
|
import qualified Network.HTTP.Types.Method as H
|
2021-04-09 04:03:21 +03:00
|
|
|
import qualified Network.Socket as N
|
2019-12-19 13:18:12 +03:00
|
|
|
import qualified Urbit.Ob as Ob
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- Misc Types ------------------------------------------------------------------
|
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
2019-08-01 00:52:49 +03:00
|
|
|
Domain Name in TLD order:
|
2019-07-24 04:34:16 +03:00
|
|
|
|
2019-08-01 00:52:49 +03:00
|
|
|
["org", "urbit", "dns"] -> dns.urbit.org
|
|
|
|
-}
|
2019-07-24 04:34:16 +03:00
|
|
|
newtype Turf = Turf { unTurf :: [Cord] }
|
|
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun)
|
|
|
|
|
2019-08-08 01:24:02 +03:00
|
|
|
newtype KingId = KingId { unKingId :: UV }
|
2019-08-02 08:07:20 +03:00
|
|
|
deriving newtype (Eq, Ord, Show, Num, Real, Enum, Integral, FromNoun, ToNoun)
|
|
|
|
|
2019-08-08 01:24:02 +03:00
|
|
|
newtype ServId = ServId { unServId :: UV }
|
2019-08-03 03:09:53 +03:00
|
|
|
deriving newtype (Eq, Ord, Show, Num, Enum, Integral, Real, FromNoun, ToNoun)
|
|
|
|
|
2020-12-03 21:41:03 +03:00
|
|
|
-- Arvo Version Negotiation ----------------------------------------------------
|
|
|
|
|
|
|
|
-- Information about the king runtime passed to Arvo.
|
|
|
|
data Vere = Vere { vereName :: Term,
|
2020-12-16 19:17:13 +03:00
|
|
|
vereRev :: [Cord],
|
2020-12-03 21:41:03 +03:00
|
|
|
vereWynn :: Wynn }
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
instance ToNoun Vere where
|
|
|
|
toNoun Vere{..} = toNoun ((vereName, vereRev), vereWynn)
|
|
|
|
|
|
|
|
instance FromNoun Vere where
|
|
|
|
parseNoun n = named "Vere" $ do
|
|
|
|
((vereName, vereRev), vereWynn) <- parseNoun n
|
|
|
|
pure $ Vere {..}
|
|
|
|
|
|
|
|
-- A list of names and their kelvin numbers, used in version negotiations.
|
2020-12-16 19:46:54 +03:00
|
|
|
newtype Wynn = Wynn { unWynn :: [(Term, Word)] }
|
2020-12-03 21:41:03 +03:00
|
|
|
deriving newtype (Eq, Ord, Show, FromNoun, ToNoun)
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
-- Http Common -----------------------------------------------------------------
|
|
|
|
|
2019-12-13 13:10:53 +03:00
|
|
|
data Header = Header Cord Bytes
|
2019-07-24 04:34:16 +03:00
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
data ResponseHeader = ResponseHeader
|
|
|
|
{ statusCode :: Word
|
|
|
|
, headers :: [Header]
|
|
|
|
}
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
data HttpEvent
|
|
|
|
= Start ResponseHeader (Maybe File) Bool
|
|
|
|
| Continue (Maybe File) Bool
|
|
|
|
| Cancel ()
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
deriveNoun ''ResponseHeader
|
|
|
|
deriveNoun ''Header
|
|
|
|
deriveNoun ''HttpEvent
|
|
|
|
|
|
|
|
|
|
|
|
-- Http Requests ---------------------------------------------------------------
|
|
|
|
|
|
|
|
type Method = H.StdMethod
|
|
|
|
|
|
|
|
-- TODO Hack! Don't write instances for library types. Write them for
|
|
|
|
-- our types instead.
|
|
|
|
|
|
|
|
instance ToNoun H.StdMethod where
|
2019-08-02 09:56:42 +03:00
|
|
|
toNoun = toNoun . MkBytes . H.renderStdMethod
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
instance FromNoun H.StdMethod where
|
|
|
|
parseNoun n = named "StdMethod" $ do
|
2019-08-02 09:56:42 +03:00
|
|
|
MkBytes bs <- parseNoun n
|
|
|
|
case H.parseMethod bs of
|
|
|
|
Left md -> fail ("Unexpected method: " <> unpack (decodeUtf8 md))
|
2019-07-24 04:34:16 +03:00
|
|
|
Right m -> pure m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Http Server Configuration ---------------------------------------------------
|
|
|
|
|
2020-04-30 21:18:16 +03:00
|
|
|
newtype PEM = PEM { unPEM :: Wain }
|
2020-06-11 01:00:31 +03:00
|
|
|
deriving newtype (Eq, Ord, ToNoun, FromNoun)
|
|
|
|
|
|
|
|
instance Show PEM where
|
|
|
|
show _ = "\"PEM (secret)\""
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
type Key = PEM
|
|
|
|
type Cert = PEM
|
|
|
|
|
|
|
|
data HttpServerConf = HttpServerConf
|
2019-08-02 09:56:42 +03:00
|
|
|
{ hscSecure :: Maybe (Key, Cert)
|
|
|
|
, hscProxy :: Bool
|
|
|
|
, hscLog :: Bool
|
|
|
|
, hscRedirect :: Bool
|
2019-07-24 04:34:16 +03:00
|
|
|
}
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
deriveNoun ''HttpServerConf
|
|
|
|
|
|
|
|
|
|
|
|
-- Desk and Mime ---------------------------------------------------------------
|
|
|
|
|
|
|
|
newtype Desk = Desk { unDesk :: Cord }
|
2020-12-19 04:00:56 +03:00
|
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun, IsString)
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
data Mime = Mime Path File
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
deriveNoun ''Mime
|
|
|
|
|
|
|
|
|
|
|
|
-- Json ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
type Json = Nullable JsonNode
|
|
|
|
|
|
|
|
data JsonNode
|
|
|
|
= JNA [Json]
|
|
|
|
| JNB Bool
|
2019-09-26 23:29:19 +03:00
|
|
|
| JNO (HoonMap Cord Json)
|
2019-07-24 04:34:16 +03:00
|
|
|
| JNN Knot
|
|
|
|
| JNS Cord
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
|
|
|
deriveNoun ''JsonNode
|
|
|
|
|
|
|
|
|
2019-12-10 05:45:19 +03:00
|
|
|
-- Ames Destinations -------------------------------------------------
|
2019-07-24 04:34:16 +03:00
|
|
|
|
2021-04-09 04:03:21 +03:00
|
|
|
serializeToNoun :: Serialize a => a -> Noun
|
|
|
|
serializeToNoun = A . bytesAtom . encode
|
|
|
|
|
|
|
|
serializeParseNoun :: Serialize a => String -> Int -> Noun -> Parser a
|
|
|
|
serializeParseNoun desc len = named (pack desc) . \case
|
|
|
|
A (atomBytes -> bs)
|
|
|
|
-- Atoms lose leading 0s, but since lsb, these become trailing NULs
|
|
|
|
| length bs <= len -> case decode $ bs <> replicate (len - length bs) 0 of
|
|
|
|
Right aa -> pure aa
|
|
|
|
Left msg -> fail msg
|
|
|
|
| otherwise -> fail ("putative " <> desc <> " " <> show bs <> " too long")
|
|
|
|
C{} -> fail ("unexpected cell in " <> desc)
|
|
|
|
|
2019-12-19 13:18:12 +03:00
|
|
|
newtype Patp a = Patp { unPatp :: a }
|
|
|
|
deriving newtype (Eq, Ord, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
|
|
|
|
2019-07-24 04:34:16 +03:00
|
|
|
-- Network Port
|
2019-07-25 04:59:45 +03:00
|
|
|
newtype Port = Port { unPort :: Word16 }
|
|
|
|
deriving newtype (Eq, Ord, Show, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
2019-07-24 04:34:16 +03:00
|
|
|
|
2019-08-01 00:52:49 +03:00
|
|
|
-- @if
|
2021-04-09 04:03:21 +03:00
|
|
|
newtype Ipv4 = Ipv4 { unIpv4 :: N.HostAddress }
|
|
|
|
deriving newtype (Eq, Ord, Enum)
|
|
|
|
|
|
|
|
instance Serialize Ipv4 where
|
2021-04-12 20:12:37 +03:00
|
|
|
get = (\a b c d -> Ipv4 $ N.tupleToHostAddress $ (d, c, b, a))
|
|
|
|
<$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8
|
|
|
|
put (Ipv4 (N.hostAddressToTuple -> (a, b, c, d))) = for_ [d, c, b, a] putWord8
|
2021-04-09 04:03:21 +03:00
|
|
|
|
|
|
|
instance ToNoun Ipv4 where
|
|
|
|
toNoun = serializeToNoun
|
|
|
|
|
|
|
|
instance FromNoun Ipv4 where
|
|
|
|
parseNoun = serializeParseNoun "Ipv4" 4
|
2021-01-11 08:19:40 +03:00
|
|
|
|
|
|
|
instance Show Ipv4 where
|
2021-04-12 20:12:37 +03:00
|
|
|
show (Ipv4 (N.hostAddressToTuple -> (a, b, c, d))) =
|
|
|
|
show a ++ "." ++
|
|
|
|
show b ++ "." ++
|
|
|
|
show c ++ "." ++
|
|
|
|
show d
|
2019-08-01 00:52:49 +03:00
|
|
|
|
|
|
|
-- @is
|
2021-04-09 04:03:21 +03:00
|
|
|
-- should probably use hostAddress6ToTuple here, but no one uses it right now
|
2019-08-01 00:52:49 +03:00
|
|
|
newtype Ipv6 = Ipv6 { unIpv6 :: Word128 }
|
|
|
|
deriving newtype (Eq, Ord, Show, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
|
|
|
|
2019-12-19 13:18:12 +03:00
|
|
|
type Galaxy = Patp Word8
|
|
|
|
|
|
|
|
instance Integral a => Show (Patp a) where
|
|
|
|
show = show . Ob.renderPatp . Ob.patp . fromIntegral . unPatp
|
2019-08-01 00:52:49 +03:00
|
|
|
|
2021-02-02 21:54:07 +03:00
|
|
|
data AmesAddress = AAIpv4 Ipv4 Port
|
2019-07-24 04:34:16 +03:00
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
2021-02-02 21:54:07 +03:00
|
|
|
instance Serialize AmesAddress where
|
2021-04-09 04:03:21 +03:00
|
|
|
get = AAIpv4 <$> get <*> (Port <$> getWord16le)
|
|
|
|
put (AAIpv4 ip (Port port)) = put ip >> putWord16le port
|
2019-08-01 00:52:49 +03:00
|
|
|
|
2021-02-02 21:54:07 +03:00
|
|
|
instance FromNoun AmesAddress where
|
2021-04-09 04:03:21 +03:00
|
|
|
parseNoun = serializeParseNoun "AmesAddress" 6
|
2021-02-02 21:54:07 +03:00
|
|
|
|
|
|
|
instance ToNoun AmesAddress where
|
2021-04-09 04:03:21 +03:00
|
|
|
toNoun = serializeToNoun
|
2021-02-02 21:54:07 +03:00
|
|
|
|
|
|
|
type AmesDest = Each Galaxy AmesAddress
|
2019-08-01 00:52:49 +03:00
|
|
|
|
2019-07-24 04:34:16 +03:00
|
|
|
|
|
|
|
-- Path+Tagged Restructuring ---------------------------------------------------
|
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
2019-07-24 07:03:04 +03:00
|
|
|
This reorganized events and effects to be easier to parse. This is
|
|
|
|
complicated and gross, and a better way should be found!
|
|
|
|
|
|
|
|
ReOrg takes in nouns with the following shape:
|
|
|
|
|
|
|
|
[[fst snd rest] [tag val]]
|
|
|
|
|
|
|
|
And turns that into:
|
|
|
|
|
|
|
|
ReOrg fst snd tag rest val
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
[//behn/5 %doze ~ 9999]
|
|
|
|
|
|
|
|
Becomes:
|
|
|
|
|
|
|
|
Reorg "" "behn" "doze" ["5"] 9999
|
|
|
|
|
|
|
|
This is convenient, since we can then use our head-tag based FromNoun
|
|
|
|
and ToNoun instances.
|
|
|
|
|
|
|
|
NOTE:
|
|
|
|
|
|
|
|
Also, in the wild, I ran into this event:
|
|
|
|
|
|
|
|
[//term/1 %init]
|
|
|
|
|
|
|
|
So, I rewrite atom-events as follows:
|
|
|
|
|
|
|
|
[x y=@] -> [x [y ~]]
|
|
|
|
|
|
|
|
Which rewrites the %init example to:
|
|
|
|
|
|
|
|
[//term/1 [%init ~]]
|
|
|
|
|
2019-07-25 04:59:45 +03:00
|
|
|
TODO The reverse translation is not done yet.
|
|
|
|
|
2019-07-24 07:03:04 +03:00
|
|
|
-}
|
|
|
|
data ReOrg = ReOrg Cord Cord Cord EvilPath Noun
|
|
|
|
|
|
|
|
instance FromNoun ReOrg where
|
|
|
|
parseNoun = named "ReOrg" . \case
|
|
|
|
A _ -> expected "got atom"
|
|
|
|
C (A _) _ -> expected "empty route"
|
|
|
|
C h (A a) -> parseNoun (C h (C (A a) (A 0)))
|
|
|
|
C (C _ (A _)) (C _ _) -> expected "route is too short"
|
|
|
|
C (C f (C s p)) (C t v) -> do
|
|
|
|
fst :: Cord <- named "first-route" $ parseNoun f
|
|
|
|
snd :: Cord <- named "second-route" $ parseNoun s
|
|
|
|
pax :: EvilPath <- named "rest-of-route" $ parseNoun p
|
|
|
|
tag :: Cord <- named "tag" $ parseNoun t
|
|
|
|
val :: Noun <- pure v
|
|
|
|
pure (ReOrg fst snd tag pax val)
|
2019-07-24 04:34:16 +03:00
|
|
|
where
|
|
|
|
expected got = fail ("expected route+tagged; " <> got)
|
|
|
|
|
2019-07-24 07:03:04 +03:00
|
|
|
instance ToNoun ReOrg where
|
|
|
|
toNoun (ReOrg fst snd tag pax val) =
|
|
|
|
toNoun ((fst, snd, pax), (tag, val))
|
2019-07-24 04:34:16 +03:00
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
2019-07-24 07:03:04 +03:00
|
|
|
Given something parsed from a ReOrg Noun, convert that back to
|
|
|
|
a ReOrg.
|
|
|
|
|
2019-07-24 04:34:16 +03:00
|
|
|
This code may crash, but only if the FromNoun/ToNoun instances for
|
|
|
|
the effects are incorrect.
|
|
|
|
-}
|
2019-07-24 07:03:04 +03:00
|
|
|
reorgThroughNoun :: ToNoun x => (Cord, x) -> ReOrg
|
|
|
|
reorgThroughNoun =
|
|
|
|
fromNounCrash . toNoun >>> \case
|
|
|
|
(f, s, t, p, v) -> ReOrg f s t p v
|
2019-07-24 04:34:16 +03:00
|
|
|
where
|
|
|
|
fromNounCrash :: FromNoun a => Noun -> a
|
|
|
|
fromNounCrash =
|
|
|
|
fromNounErr >>> \case
|
|
|
|
Left err -> error (show err)
|
|
|
|
Right vl -> vl
|