Skip to content
Main.hs 1.82 KiB
Newer Older
Yuanle Song's avatar
Yuanle Song committed
{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative ((<|>), liftA2)
import Data.Monoid ((<>))
Yuanle Song's avatar
Yuanle Song committed
import Data.Maybe (fromMaybe)
Yuanle Song's avatar
Yuanle Song committed
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as L
Yuanle Song's avatar
Yuanle Song committed
import Text.Read (readMaybe)
import System.Environment (lookupEnv)
import Network.HTTP.Types (StdMethod(HEAD))
Yuanle Song's avatar
Yuanle Song committed
import Web.Scotty

getClientIP1 :: ActionM (Maybe Text)
getClientIP1 = header "CLIENT-IP"
Yuanle Song's avatar
Yuanle Song committed

getClientIP2 :: ActionM (Maybe Text)
getClientIP2 =
Yuanle Song's avatar
Yuanle Song committed
  fmap (fmap firstElement) (header "X-FORWARDED-FOR") where
Yuanle Song's avatar
Yuanle Song committed
      firstElement :: Text -> Text
      firstElement = head . L.splitOn ","

getClientIP3 :: ActionM (Maybe Text)
getClientIP3 = foldl1 (liftA2 (<|>)) $ map header ["X-FORWARDED"
                                                  ,"X-CLUSTER-CLIENT-IP"
                                                  ,"FORWARDED-FOR"
                                                  ,"FORWARDED"
                                                  ,"REMOTE-ADDR"]
Yuanle Song's avatar
Yuanle Song committed

getClientIP :: ActionM ()
getClientIP = do
  ip <- foldl1 (liftA2 (<|>)) [getClientIP1, getClientIP2, getClientIP3]
  case ip of
    Nothing -> text "unknown\n"
    Just rawIp -> text $ rawIp <> "\n"

Yuanle Song's avatar
Yuanle Song committed
-- | look up an environment variable, if it doesn't exist or is empty, use
-- default value instead. Otherwise, call a reader function on it and use that
-- value if parse is okay, use default value if parse fail.
getEnvDefault :: String -> a -> (String -> Maybe a) -> IO a
getEnvDefault variable defaultValue stringReader = do
    r <- lookupEnv variable
    return $ case r of
      Nothing -> defaultValue
      Just [] -> defaultValue
Yuanle Song's avatar
Yuanle Song committed
      Just xs -> fromMaybe defaultValue (stringReader xs)
Yuanle Song's avatar
Yuanle Song committed

getListenPort :: IO Int
getListenPort = getEnvDefault "PORT" 8081 readMaybe

Yuanle Song's avatar
Yuanle Song committed
main :: IO ()
Yuanle Song's avatar
Yuanle Song committed
main = do
  port <- getListenPort
  scotty port $ do
Yuanle Song's avatar
Yuanle Song committed
    get "/" getClientIP
    addroute HEAD "/" $ return ()