1

我是 Erlang 世界的新手,我正在尝试为 Twitter Stream API 编写一个客户端。我正在使用 httpc:request 发出 POST 请求,但我经常收到 401 错误,我显然在发送请求的方式上做错了......我所拥有的看起来像这样:

fetch_data() ->
    Method = post,
    URL = "https://stream.twitter.com/1.1/statuses/filter.json",
    Headers = "Authorization: OAuth oauth_consumer_key=\"XXX\", oauth_nonce=\"XXX\", oauth_signature=\"XXX%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"XXX\", oauth_token=\"XXX-XXXXX\", oauth_version=\"1.0\"",
    ContentType = "application/json",
    Body = "{\"track\":\"keyword\"}",
    HTTPOptions = [],
    Options = [],
    R = httpc:request(Method, {URL, Headers, ContentType, Body}, HTTPOptions, Options),
    R.

在这一点上,我确信签名没有问题,因为在尝试使用 curl 访问 API 时,相同的签名可以正常工作。我猜我提出请求的方式存在一些问题。

我对请求的响应按照上面演示的方式是:

{ok,{{"HTTP/1.1",401,"Unauthorized"},
 [{"cache-control","must-revalidate,no-cache,no-store"},
  {"connection","close"},
  {"www-authenticate","Basic realm=\"Firehose\""},
  {"content-length","1243"},
  {"content-type","text/html"}],
 "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n<title>Error 401 Unauthorized</title>\n</head>\n<body>\n<h2>HTTP ERROR: 401</h2>\n<p>Problem accessing '/1.1/statuses/filter.json'. Reason:\n<pre>    Unauthorized</pre>\n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n</body>\n</html>\n"}}

尝试使用 curl 时,我正在使用它:

curl --request 'POST' 'https://stream.twitter.com/1.1/statuses/filter.json' --data 'track=keyword' --header 'Authorization: OAuth oauth_consumer_key="XXX", oauth_nonce="XXX", oauth_signature="XXX%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="XXX", oauth_token="XXX-XXXX", oauth_version="1.0"' --verbose

我得到的事件就好了。

对此的任何帮助将不胜感激,Erlang 的新手,我已经在这个问题上拉了很长一段时间了。

4

1 回答 1

5

您的代码有几个问题

  1. 在 Erlang 中,您将参数编码为 JSON 主体,而使用curl,您将它们编码为表单数据 ( application/x-www-form-urlencoded)。Twitter API 期望后者。事实上,你得到一个 401 因为 OAuth 签名不匹配,因为你track=keyword在计算中包含了参数,而 Twitter 的服务器在没有 JSON 正文的情况下计算它,因为它应该按照OAuth RFC

  2. 您正在使用httpc默认选项。这不适用于流 API,因为流永远不会结束。您需要在结果到达时对其进行处理。为此,您需要将 {sync, false}选项传递给httpc. 另请参阅streamreceiver选项。

最终,虽然httpc最初可以访问 Twitter 流式 API,但它对您需要围绕它开发以从 Twitter API 流式传输的代码几乎没有价值。根据您的需要,您可能希望将其替换为直接构建的简单客户端ssl,特别是考虑到它可以解码 HTTP 数据包(留给您的是 HTTP 块编码)。

例如,如果您的关键字很少见,您可能会从httpc. 此外,如果没有httpc.

直接基于的流式客户端ssl可以实现为gen_server(或简单的过程,如果您不遵循 OTP 原则)甚至更好gen_fsm地实现重新连接策略。您可以进行如下操作:

  • 连接使用ssl:connect/3,4指定您希望套接字解码 HTTP 数据包{packet, http_bin}并希望套接字配置为被动模式{active, false}

  • 发送带有ssl:send/2,3. 它应该分布在用 CRLF ( ) 分隔的几行上\r\n,首先是查询行 ( GET /1.1/statuses/filter.json?... HTTP/1.1),然后是包含 OAuth 标头的标头。确保你也包括在内Host: stream.twitter.com。以空行结束。

  • 接收 HTTP 响应。您可以通过循环来实现这一点(因为套接字处于被动模式),一直调用ssl:recv/2,3直到获得http_eoh(标头结束)。Transfer-Encoding通过查看响应标头,记下服务器是否会向您发送分块数据。

  • 将套接字配置为活动模式,ssl:setopts/2并指定您希望数据包为原始数据和二进制格式的数据。事实上,如果数据被分块,您可以继续在被动模式下使用套接字。您还可以逐行获取数据或以字符串形式获取数据。这是一个口味问题:raw 是最安全的选择,逐行要求您检查缓冲区大小以防止截断较长的 JSON 编码推文。

  • 从 Twitter 接收数据作为发送到您的流程的消息,可以使用receive(简单流程)或在handle_info处理程序中(如果您使用 a 实现gen_server)。如果数据被分块,您将首先收到块大小,然后是推文和块的结尾(参见RFC 2616)。准备好将推文分散在几个块上(即维护某种缓冲区)。最好的办法是在这个过程中进行最少的解码,然后将推文发送到另一个过程,可能是二进制格式。

您还应该处理错误和被 Twitter 关闭的套接字。确保遵循Twitter 的重新连接指南

于 2013-09-28T12:06:50.057 回答