0

我想做的是创建一个有点智能的反向代理服务器,它应该自己处理一些请求并将其他请求转发到选择的后端。为了使它具有挑战性,我正在努力在 Haskell 中做到这一点,我完全是新手。

这是我到目前为止提出的代码:

{-# 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

4

1 回答 1

1

IOinIO 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”。

您可能会发现构建一个ApplicationinIO并将其交给其他地方在概念上更清晰,我不会与您争论。不过我想指出,您选择Application基于Request, 所以在某种程度上您在选择时处于假设的 HTTP monad 中,所以lookupServeFunction的签名Request -> Application对我来说更有意义。

如果您想为 , 保留该类型签名proxyApplookupServeFunction并且app也必须在其中IO,并且main必须相应地进行更改,例如

myApp <- app
...

正如 haoformayor 所说,没有外层通常更容易工作IO


您可能还想简化main.

fmap logStdoutDev (return app)

是相同的

return (logStdoutDev app)

并运行 3011 =<< return (logStdoutDev app)

是相同的

run 3011 (logStdoutDev app)

您可能想要安装 hlint,这将帮助您发现这些。

于 2015-12-28T13:26:56.383 回答