我想使用 Haskell 直接使用Bloodhound或 REST api 向 Amazon 托管的弹性搜索服务器发送请求。
由于服务器具有基于 IAM 的访问策略,我需要签署签名。为了签署每个请求的签名,需要计算有效负载的哈希值。我不确定如何将 RequestBody(分块或不分块)放入 ByteString。
我想使用 Haskell 直接使用Bloodhound或 REST api 向 Amazon 托管的弹性搜索服务器发送请求。
由于服务器具有基于 IAM 的访问策略,我需要签署签名。为了签署每个请求的签名,需要计算有效负载的哈希值。我不确定如何将 RequestBody(分块或不分块)放入 ByteString。
对于您的特定用例:在我看来,该wreq
库已经支持 AWS IAM 签名(教程),因此根据您的特定要求,使用它/查看它是如何工作的可能是最简单的。
看起来使用RequestBody
s 最简单的方法实际上可能只是编写一个函数来计算 6 种可能的RequestBody
s 中的每一种的签名,或者至少是你需要的那些,而不是尝试重用 http-client 的机器来将 6 个中的任何一个转换为 1 个ByteString
。这是一个特别有用的选项,因为看起来您可能需要为分块请求做一些特殊的事情。a can 的选项只有几个RequestBody
,而且只有两个(基于流的)似乎很难使用;这些通常也应该得到特殊处理(特别是因为,取决于请求的创建者如何实现它们,我不清楚是否保证可以从它们中读取并让它们以后仍然工作)。可能对这种方法有用。
根据您使用 Haskell 的经验,流构造函数可能有点吓人。然而它实际上还不错:扩展类型同义词给出了GivesPopper () = (IO ByteString -> IO ()) -> IO ()
. 该IO ByteString -> IO ()
函数是您可以提供的东西,它接受流块的生产者(每次评估将产生一个多块)并用它做一些有用的事情——例如,将该块写入一个列表中,IORef
以便您可以检查它之后。如果你GivesPopper
在这个函数上调用 ,你会得到一个IO
以有用的生产者作为参数运行它的动作。例如:
foo :: NeedsPopper ()
foo pop = do
chunk <- pop
if (chunk == "") then return ()
else BS.putStr chunk >> foo pop
将在传递给 a 时GivesPopper ()
将流式响应正文打印到标准输出。
如果您希望可以多次构建请求而不会出现问题(任何 stream 都GivesPopper
必须可以多次调用,等等),并且您希望重用http-client
的内部响应呈现,那么您可能能够摆脱一些非常骇人听闻的事情,例如这个:
getRequest :: Request -> IO BS.ByteString
getRequest req = do
(conn, out, inp) <- dummyConnection []
let req' = req { requestHeaders = (CI.mk "Expect", "100-continue")
: requestHeaders req
}
(Just later) <- requestBuilder req' conn
_ <- out
later
BS.concat <$> out
似乎唯一http-client
呈现 a的地方Response
是 in requestBuilder
,并且在构建请求时,这将始终发送标头,我认为这不是您想要的。该_ <- out
行从虚拟连接中清除了 header+body,并且,既然Expect: 100-continue
给出了,later
那么应该再次将 body 写入虚拟连接。请注意,这只适用于可以多次构建响应而没有问题的情况。如果您的请求实际上希望将该continue
功能用于不同的东西,那么这可能效果不佳。另请注意,这将写出分块请求的编码版本(例如"6\r\na body\r\n0\r\n\r\n"
),这可能是也可能不是您想要的。