Проблемы ввода-вывода при попытке реализовать 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)
Возможно, вы захотите установить подсказку, которая поможет вам определить это.