{-# LANGUAGE FlexibleContexts #-}

module Network.AWS.XRayClient.TraceId
  ( amazonTraceIdHeaderName
  , -- * Trace ID
    XRayTraceId(..)
  , generateXRayTraceId
  , makeXRayTraceId
  , XRaySegmentId(..)
  , generateXRaySegmentId
    -- * Trace ID Header
  , XRayTraceIdHeaderData(..)
  , xrayTraceIdHeaderData
  , parseXRayTraceIdHeaderData
  , makeXRayTraceIdHeaderValue
  ) where

import Prelude

import Control.DeepSeq (NFData)
import Data.Aeson
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS8
import Data.Char (intToDigit)
import Data.IORef
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Time.Clock.POSIX (getPOSIXTime)
import GHC.Generics
import Network.HTTP.Types.Header
import Numeric (showHex)
import System.Random
import System.Random.XRayCustom

-- | Variable for "X-Amzn-Trace-Id" so you don't have to worry about
-- misspelling it.
amazonTraceIdHeaderName :: HeaderName
amazonTraceIdHeaderName :: HeaderName
amazonTraceIdHeaderName = HeaderName
"X-Amzn-Trace-Id"

-- | A trace_id consists of three numbers separated by hyphens. For example,
-- 1-58406520-a006649127e371903a2de979. This includes: The version number, that
-- is, 1. The time of the original request, in Unix epoch time, in 8
-- hexadecimal digits. For example, 10:00AM December 2nd, 2016 PST in epoch
-- time is 1480615200 seconds, or 58406520 in hexadecimal. A 96-bit identifier
-- for the trace, globally unique, in 24 hexadecimal digits.
newtype XRayTraceId = XRayTraceId { XRayTraceId -> Text
unXRayTraceId :: Text }
  deriving (Int -> XRayTraceId -> ShowS
[XRayTraceId] -> ShowS
XRayTraceId -> String
(Int -> XRayTraceId -> ShowS)
-> (XRayTraceId -> String)
-> ([XRayTraceId] -> ShowS)
-> Show XRayTraceId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [XRayTraceId] -> ShowS
$cshowList :: [XRayTraceId] -> ShowS
show :: XRayTraceId -> String
$cshow :: XRayTraceId -> String
showsPrec :: Int -> XRayTraceId -> ShowS
$cshowsPrec :: Int -> XRayTraceId -> ShowS
Show, XRayTraceId -> XRayTraceId -> Bool
(XRayTraceId -> XRayTraceId -> Bool)
-> (XRayTraceId -> XRayTraceId -> Bool) -> Eq XRayTraceId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: XRayTraceId -> XRayTraceId -> Bool
$c/= :: XRayTraceId -> XRayTraceId -> Bool
== :: XRayTraceId -> XRayTraceId -> Bool
$c== :: XRayTraceId -> XRayTraceId -> Bool
Eq)
  deriving newtype (Value -> Parser [XRayTraceId]
Value -> Parser XRayTraceId
(Value -> Parser XRayTraceId)
-> (Value -> Parser [XRayTraceId]) -> FromJSON XRayTraceId
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [XRayTraceId]
$cparseJSONList :: Value -> Parser [XRayTraceId]
parseJSON :: Value -> Parser XRayTraceId
$cparseJSON :: Value -> Parser XRayTraceId
FromJSON, [XRayTraceId] -> Encoding
[XRayTraceId] -> Value
XRayTraceId -> Encoding
XRayTraceId -> Value
(XRayTraceId -> Value)
-> (XRayTraceId -> Encoding)
-> ([XRayTraceId] -> Value)
-> ([XRayTraceId] -> Encoding)
-> ToJSON XRayTraceId
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [XRayTraceId] -> Encoding
$ctoEncodingList :: [XRayTraceId] -> Encoding
toJSONList :: [XRayTraceId] -> Value
$ctoJSONList :: [XRayTraceId] -> Value
toEncoding :: XRayTraceId -> Encoding
$ctoEncoding :: XRayTraceId -> Encoding
toJSON :: XRayTraceId -> Value
$ctoJSON :: XRayTraceId -> Value
ToJSON, XRayTraceId -> ()
(XRayTraceId -> ()) -> NFData XRayTraceId
forall a. (a -> ()) -> NFData a
rnf :: XRayTraceId -> ()
$crnf :: XRayTraceId -> ()
NFData)

-- TODO: Make a parse function and don't export constructor
-- parseXRayTraceId :: (MonadError String m) => ByteString -> m XRayTraceId

-- | Generates an 'XRayTraceId' in 'IO'. WARNING: This uses the global
-- 'StdGen', so this can be a bottleneck in multi-threaded applications.
generateXRayTraceId :: IORef StdGen -> IO XRayTraceId
generateXRayTraceId :: IORef StdGen -> IO XRayTraceId
generateXRayTraceId IORef StdGen
ioRef = do
  Int
timeInSeconds <- POSIXTime -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (POSIXTime -> Int) -> IO POSIXTime -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO POSIXTime
getPOSIXTime
  IORef StdGen -> (StdGen -> (XRayTraceId, StdGen)) -> IO XRayTraceId
forall g a. RandomGen g => IORef g -> (g -> (a, g)) -> IO a
withRandomGenIORef IORef StdGen
ioRef ((StdGen -> (XRayTraceId, StdGen)) -> IO XRayTraceId)
-> (StdGen -> (XRayTraceId, StdGen)) -> IO XRayTraceId
forall a b. (a -> b) -> a -> b
$ Int -> StdGen -> (XRayTraceId, StdGen)
makeXRayTraceId Int
timeInSeconds

makeXRayTraceId :: Int -> StdGen -> (XRayTraceId, StdGen)
makeXRayTraceId :: Int -> StdGen -> (XRayTraceId, StdGen)
makeXRayTraceId Int
timeInSeconds StdGen
gen = (String -> XRayTraceId)
-> (String, StdGen) -> (XRayTraceId, StdGen)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> XRayTraceId
make ((String, StdGen) -> (XRayTraceId, StdGen))
-> (String, StdGen) -> (XRayTraceId, StdGen)
forall a b. (a -> b) -> a -> b
$ Int -> StdGen -> (String, StdGen)
randomHexString Int
24 StdGen
gen
 where
  make :: String -> XRayTraceId
make String
hexString =
    Text -> XRayTraceId
XRayTraceId (Text -> XRayTraceId) -> Text -> XRayTraceId
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"1-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> ShowS
forall a. (Integral a, Show a) => a -> ShowS
showHex Int
timeInSeconds String
"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
hexString

-- | Generates a random hexadecimal string of a given length.
randomHexString :: Int -> StdGen -> (String, StdGen)
randomHexString :: Int -> StdGen -> (String, StdGen)
randomHexString Int
n StdGen
gen =
  Int -> StdGen -> (StdGen -> (Char, StdGen)) -> (String, StdGen)
forall g a. RandomGen g => Int -> g -> (g -> (a, g)) -> ([a], g)
replicateRandom Int
n StdGen
gen ((StdGen -> (Char, StdGen)) -> (String, StdGen))
-> (StdGen -> (Char, StdGen)) -> (String, StdGen)
forall a b. (a -> b) -> a -> b
$ (Int -> Char) -> (Int, StdGen) -> (Char, StdGen)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Int -> Char
intToDigit ((Int, StdGen) -> (Char, StdGen))
-> (StdGen -> (Int, StdGen)) -> StdGen -> (Char, StdGen)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, Int) -> StdGen -> (Int, StdGen)
forall a g. (Random a, RandomGen g) => (a, a) -> g -> (a, g)
randomR (Int
0, Int
15)

-- | A 64-bit identifier for the segment, unique among segments in the same
-- trace, in 16 hexadecimal digits.
newtype XRaySegmentId = XRaySegmentId { XRaySegmentId -> Text
unXRaySegmentId :: Text }
  deriving (Int -> XRaySegmentId -> ShowS
[XRaySegmentId] -> ShowS
XRaySegmentId -> String
(Int -> XRaySegmentId -> ShowS)
-> (XRaySegmentId -> String)
-> ([XRaySegmentId] -> ShowS)
-> Show XRaySegmentId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [XRaySegmentId] -> ShowS
$cshowList :: [XRaySegmentId] -> ShowS
show :: XRaySegmentId -> String
$cshow :: XRaySegmentId -> String
showsPrec :: Int -> XRaySegmentId -> ShowS
$cshowsPrec :: Int -> XRaySegmentId -> ShowS
Show, XRaySegmentId -> XRaySegmentId -> Bool
(XRaySegmentId -> XRaySegmentId -> Bool)
-> (XRaySegmentId -> XRaySegmentId -> Bool) -> Eq XRaySegmentId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: XRaySegmentId -> XRaySegmentId -> Bool
$c/= :: XRaySegmentId -> XRaySegmentId -> Bool
== :: XRaySegmentId -> XRaySegmentId -> Bool
$c== :: XRaySegmentId -> XRaySegmentId -> Bool
Eq)
  deriving newtype (Value -> Parser [XRaySegmentId]
Value -> Parser XRaySegmentId
(Value -> Parser XRaySegmentId)
-> (Value -> Parser [XRaySegmentId]) -> FromJSON XRaySegmentId
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [XRaySegmentId]
$cparseJSONList :: Value -> Parser [XRaySegmentId]
parseJSON :: Value -> Parser XRaySegmentId
$cparseJSON :: Value -> Parser XRaySegmentId
FromJSON, [XRaySegmentId] -> Encoding
[XRaySegmentId] -> Value
XRaySegmentId -> Encoding
XRaySegmentId -> Value
(XRaySegmentId -> Value)
-> (XRaySegmentId -> Encoding)
-> ([XRaySegmentId] -> Value)
-> ([XRaySegmentId] -> Encoding)
-> ToJSON XRaySegmentId
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [XRaySegmentId] -> Encoding
$ctoEncodingList :: [XRaySegmentId] -> Encoding
toJSONList :: [XRaySegmentId] -> Value
$ctoJSONList :: [XRaySegmentId] -> Value
toEncoding :: XRaySegmentId -> Encoding
$ctoEncoding :: XRaySegmentId -> Encoding
toJSON :: XRaySegmentId -> Value
$ctoJSON :: XRaySegmentId -> Value
ToJSON, XRaySegmentId -> ()
(XRaySegmentId -> ()) -> NFData XRaySegmentId
forall a. (a -> ()) -> NFData a
rnf :: XRaySegmentId -> ()
$crnf :: XRaySegmentId -> ()
NFData)

-- TODO: Make parse function for XRaySegmentId so it is safer.

-- | Generates an 'XRaySegmentId' using a given 'StdGen'.
generateXRaySegmentId :: StdGen -> (XRaySegmentId, StdGen)
generateXRaySegmentId :: StdGen -> (XRaySegmentId, StdGen)
generateXRaySegmentId = (String -> XRaySegmentId)
-> (String, StdGen) -> (XRaySegmentId, StdGen)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (Text -> XRaySegmentId
XRaySegmentId (Text -> XRaySegmentId)
-> (String -> Text) -> String -> XRaySegmentId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack) ((String, StdGen) -> (XRaySegmentId, StdGen))
-> (StdGen -> (String, StdGen))
-> StdGen
-> (XRaySegmentId, StdGen)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> StdGen -> (String, StdGen)
randomHexString Int
16

-- | This holds the data from the @X-Amzn-Trace-Id@ header
--
-- See <http://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader>
--
data XRayTraceIdHeaderData = XRayTraceIdHeaderData
  { XRayTraceIdHeaderData -> XRayTraceId
xrayTraceIdHeaderDataRootTraceId :: !XRayTraceId
  , XRayTraceIdHeaderData -> Maybe XRaySegmentId
xrayTraceIdHeaderDataParentId :: !(Maybe XRaySegmentId)
  , XRayTraceIdHeaderData -> Maybe Bool
xrayTraceIdHeaderDataSampled :: !(Maybe Bool)
  }
  deriving (Int -> XRayTraceIdHeaderData -> ShowS
[XRayTraceIdHeaderData] -> ShowS
XRayTraceIdHeaderData -> String
(Int -> XRayTraceIdHeaderData -> ShowS)
-> (XRayTraceIdHeaderData -> String)
-> ([XRayTraceIdHeaderData] -> ShowS)
-> Show XRayTraceIdHeaderData
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [XRayTraceIdHeaderData] -> ShowS
$cshowList :: [XRayTraceIdHeaderData] -> ShowS
show :: XRayTraceIdHeaderData -> String
$cshow :: XRayTraceIdHeaderData -> String
showsPrec :: Int -> XRayTraceIdHeaderData -> ShowS
$cshowsPrec :: Int -> XRayTraceIdHeaderData -> ShowS
Show, XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool
(XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool)
-> (XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool)
-> Eq XRayTraceIdHeaderData
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool
$c/= :: XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool
== :: XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool
$c== :: XRayTraceIdHeaderData -> XRayTraceIdHeaderData -> Bool
Eq, (forall x. XRayTraceIdHeaderData -> Rep XRayTraceIdHeaderData x)
-> (forall x. Rep XRayTraceIdHeaderData x -> XRayTraceIdHeaderData)
-> Generic XRayTraceIdHeaderData
forall x. Rep XRayTraceIdHeaderData x -> XRayTraceIdHeaderData
forall x. XRayTraceIdHeaderData -> Rep XRayTraceIdHeaderData x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep XRayTraceIdHeaderData x -> XRayTraceIdHeaderData
$cfrom :: forall x. XRayTraceIdHeaderData -> Rep XRayTraceIdHeaderData x
Generic)

-- | Constructor for 'XRayTraceIdHeaderData'.
xrayTraceIdHeaderData :: XRayTraceId -> XRayTraceIdHeaderData
xrayTraceIdHeaderData :: XRayTraceId -> XRayTraceIdHeaderData
xrayTraceIdHeaderData XRayTraceId
traceId = XRayTraceIdHeaderData :: XRayTraceId
-> Maybe XRaySegmentId -> Maybe Bool -> XRayTraceIdHeaderData
XRayTraceIdHeaderData
  { xrayTraceIdHeaderDataRootTraceId :: XRayTraceId
xrayTraceIdHeaderDataRootTraceId = XRayTraceId
traceId
  , xrayTraceIdHeaderDataParentId :: Maybe XRaySegmentId
xrayTraceIdHeaderDataParentId = Maybe XRaySegmentId
forall a. Maybe a
Nothing
  , xrayTraceIdHeaderDataSampled :: Maybe Bool
xrayTraceIdHeaderDataSampled = Maybe Bool
forall a. Maybe a
Nothing
  }

-- | Try to parse the value of the X-Amzn-Trace-Id into a
-- 'XRayTraceIdHeaderData'.
parseXRayTraceIdHeaderData :: ByteString -> Maybe XRayTraceIdHeaderData
parseXRayTraceIdHeaderData :: ByteString -> Maybe XRayTraceIdHeaderData
parseXRayTraceIdHeaderData ByteString
rawHeader = do
  [(ByteString, ByteString)]
components <- (ByteString -> Maybe (ByteString, ByteString))
-> [ByteString] -> Maybe [(ByteString, ByteString)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ByteString -> Maybe (ByteString, ByteString)
parseHeaderComponent ([ByteString] -> Maybe [(ByteString, ByteString)])
-> [ByteString] -> Maybe [(ByteString, ByteString)]
forall a b. (a -> b) -> a -> b
$ Char -> ByteString -> [ByteString]
BS8.split Char
';' ByteString
rawHeader
  ByteString
traceId <- ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"Root" [(ByteString, ByteString)]
components
  XRayTraceIdHeaderData -> Maybe XRayTraceIdHeaderData
forall (f :: * -> *) a. Applicative f => a -> f a
pure XRayTraceIdHeaderData :: XRayTraceId
-> Maybe XRaySegmentId -> Maybe Bool -> XRayTraceIdHeaderData
XRayTraceIdHeaderData
    { xrayTraceIdHeaderDataRootTraceId :: XRayTraceId
xrayTraceIdHeaderDataRootTraceId = Text -> XRayTraceId
XRayTraceId (ByteString -> Text
T.decodeUtf8 ByteString
traceId)
    , xrayTraceIdHeaderDataParentId :: Maybe XRaySegmentId
xrayTraceIdHeaderDataParentId =
      Text -> XRaySegmentId
XRaySegmentId (Text -> XRaySegmentId)
-> (ByteString -> Text) -> ByteString -> XRaySegmentId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
T.decodeUtf8 (ByteString -> XRaySegmentId)
-> Maybe ByteString -> Maybe XRaySegmentId
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"Parent" [(ByteString, ByteString)]
components
    , xrayTraceIdHeaderDataSampled :: Maybe Bool
xrayTraceIdHeaderDataSampled = ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"Sampled" [(ByteString, ByteString)]
components Maybe ByteString -> (ByteString -> Maybe Bool) -> Maybe Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ByteString -> Maybe Bool
readSampled
    }
 where
  readSampled :: ByteString -> Maybe Bool
  readSampled :: ByteString -> Maybe Bool
readSampled ByteString
"0" = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
  readSampled ByteString
"1" = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
  readSampled ByteString
_ = Maybe Bool
forall a. Maybe a
Nothing

-- | Turns a 'XRayTraceIdHeaderData' into a 'ByteString' meant for the
-- X-Amzn-Trace-Id header value.
makeXRayTraceIdHeaderValue :: XRayTraceIdHeaderData -> ByteString
makeXRayTraceIdHeaderValue :: XRayTraceIdHeaderData -> ByteString
makeXRayTraceIdHeaderValue XRayTraceIdHeaderData {Maybe Bool
Maybe XRaySegmentId
XRayTraceId
xrayTraceIdHeaderDataSampled :: Maybe Bool
xrayTraceIdHeaderDataParentId :: Maybe XRaySegmentId
xrayTraceIdHeaderDataRootTraceId :: XRayTraceId
xrayTraceIdHeaderDataSampled :: XRayTraceIdHeaderData -> Maybe Bool
xrayTraceIdHeaderDataParentId :: XRayTraceIdHeaderData -> Maybe XRaySegmentId
xrayTraceIdHeaderDataRootTraceId :: XRayTraceIdHeaderData -> XRayTraceId
..} =
  ByteString
traceIdPart ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
parentPart ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
sampledPart
 where
  traceIdPart :: ByteString
traceIdPart =
    ByteString
"Root=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Text -> ByteString
T.encodeUtf8 (XRayTraceId -> Text
unXRayTraceId XRayTraceId
xrayTraceIdHeaderDataRootTraceId)
  parentPart :: ByteString
parentPart = ByteString
-> (XRaySegmentId -> ByteString)
-> Maybe XRaySegmentId
-> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
    ByteString
""
    ((ByteString
";Parent=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>) (ByteString -> ByteString)
-> (XRaySegmentId -> ByteString) -> XRaySegmentId -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
T.encodeUtf8 (Text -> ByteString)
-> (XRaySegmentId -> Text) -> XRaySegmentId -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. XRaySegmentId -> Text
unXRaySegmentId)
    Maybe XRaySegmentId
xrayTraceIdHeaderDataParentId
  sampledPart :: ByteString
sampledPart = ByteString -> (Bool -> ByteString) -> Maybe Bool -> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
    ByteString
""
    ((ByteString
";Sampled=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<>) (ByteString -> ByteString)
-> (Bool -> ByteString) -> Bool -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString
BS8.pack (String -> ByteString) -> (Bool -> String) -> Bool -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String
forall a. Show a => a -> String
show (Int -> String) -> (Bool -> Int) -> Bool -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Int
forall a. Enum a => a -> Int
fromEnum)
    Maybe Bool
xrayTraceIdHeaderDataSampled

-- | Header components look like Name=Value
parseHeaderComponent :: ByteString -> Maybe (ByteString, ByteString)
parseHeaderComponent :: ByteString -> Maybe (ByteString, ByteString)
parseHeaderComponent ByteString
rawComponent = case Char -> ByteString -> [ByteString]
BS8.split Char
'=' ByteString
rawComponent of
  [ByteString
name, ByteString
value] -> (ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
Just (ByteString
name, ByteString
value)
  [ByteString]
_ -> Maybe (ByteString, ByteString)
forall a. Maybe a
Nothing