Проблемы ввода-вывода при попытке реализовать WAI HTTP-сервер + резервный прокси

То, что я пытаюсь сделать, - это создать несколько умный обратный прокси-сервер, который должен обрабатывать некоторые запросы самостоятельно и перенаправлять другие на серверную часть по своему выбору. Чтобы сделать это сложным, я изо всех сил стараюсь сделать это в Хаскеле, в котором я абсолютный новичок.

Вот код, который я дошел до сих пор:

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Data.ByteString
import Network.HTTP.ReverseProxy
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Handler.Warp
import Network.Wai.Middleware.RequestLogger
import qualified Network.HTTP.Client as HC

helloApp :: Application
helloApp req respond =
  respond $ responseLBS status200 [("Content-Type", "text/plain")] "Hello"

proxyStubApp :: Application
proxyStubApp req respond =
  respond $ responseLBS status200 [("Content-Type", "text/plain")] "You've hit the stub"

proxyApp :: IO Application
proxyApp = do
  manager <- HC.newManager HC.defaultManagerSettings
  return $ waiProxyTo (const $ return $ WPRProxyDest ProxyDest { pdHost = "localhost", pdPort = 9393 }) defaultOnExc manager

app :: Application
app req respond =
  serve req respond
    where serve = lookupServeFunction req

lookupServeFunction :: Request -> Application
lookupServeFunction req
  | isInfixOf "sample_path" (rawPathInfo req) = proxyStubApp
  | otherwise                                 = helloApp

main = run 3011 =<< (logStdoutDev <$> return app)

Работает нормально, но когда меняю proxyStubApp для фактического proxyApp Я вынужден добавить IO повсюду. Особенно это добавляется к app, следовательно, оставив мне следующее сообщение об ошибке компиляции:

Couldn't match expected type ‘Request -> t5 -> t4’
            with actual type ‘IO Application’
The equation(s) for ‘app’ have two arguments,
but its type ‘IO Application’ has none

Мне кажется, я понимаю, почему это происходит, но у меня нет идей, как с этим справиться:(Или я что-то делаю совершенно неправильно?

Спасибо!

PS Вот зависимости, если вы хотите скомпилировать эту вещь самостоятельно: wai warp http-types text bytestring wai-extra time http-reverse-proxy http-client

1 ответ

IO в IO Application является излишним. Обратите внимание, что

type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

Итак, расширяясь proxyAppаргументы (что вы уже делаете в proxyStubApp), ты получаешь

proxyApp :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
proxyApp req continuation = do
     manager <- HC.newManager HC.defaultManagerSettings
     waiProxyTo (...) req respond

Это работает, потому что в любом случае

proxyApp :: IO Application
proxyApp = do
   manager <- HC.newManager ...
   ...        

а также

proxyApp :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
proxyApp req continuation = do
   manager <- HC.newManager ...
   ...

действие IO HC.newManager ... это "бежать в IO".

Вам может показаться, что концептуально понятнее построить Application в IO и передай это в другое место, и я не буду с тобой спорить. Я хочу отметить, что вы выбираете Application на основе Requestтаким образом, вы находитесь в гипотетической HTTP-монаде при выборе, так lookupServeFunctionподпись Request -> Application имеет больше смысла для меня.

Если вы хотите сохранить подпись этого типа для proxyApp, lookupServeFunction а также app должен быть в IO а также и main придется изменить соответственно, например

myApp <- app
...

Как сказал haoformayor, как правило, легче работать без внешнего IO слой.


Вы также можете упростить main,

fmap logStdoutDev (return app)

такой же как

return (logStdoutDev app)

и запустите 3011 =<< return (приложение logStdoutDev)

такой же как

run 3011 (logStdoutDev app)

Возможно, вы захотите установить подсказку, которая поможет вам определить это.

Другие вопросы по тегам