好吧,这将是一个相当晦涩的话题,但我会尝试一下,也许有人会知道答案。
我正在为传输 BitTorrent 客户端编写一个小的远程 Node.js 客户端。使用 JSON 对象通过 RPC 处理通信。
这是规范。
我的代码(用 CoffeeScript 编写,如果这是一个问题,我也可以提供等效的 JavaScript 代码,只是不想让这个问题太长而无法阅读),重要的部分:
runRemoteCommand: (params) ->
# convert JSON object to string
params = JSON.stringify params #, null, " "
# set request options
options =
host: @config.host
port: @config.port
path: @config.rpcPath
auth: "#{@config.username}:#{@config.password}"
headers:
'Content-Type': 'application/json'
'Content-Length': params.length
method: 'GET'
# we don't know the session id yet
sessionId = false
# wrapped in a function so it could be run two times, not really a finished solution
run = () =>
# If the session id is provided, set the header, as in 2.3.1 of the specs
if sessionId
options.headers["X-Transmission-Session-Id"] = sessionId
# define the request object and a callback for the response
request = @http.get options, (response) =>
# log everything for debug purposes
console.log "STATUS: #{response.statusCode}"
console.log "HEADERS: #{JSON.stringify response.headers}"
response.setEncoding 'utf8'
response.on "data", (data) =>
console.log "BODY: #{data}"
# if status code is 409, use provided session id
if response.statusCode == 409
sessionId = response.headers["x-transmission-session-id"]
console.log "sessionId: #{sessionId}"
# running it immediately sometimes caused the remote server to provide a 501 error, so I gave it a timeout
setTimeout run, 5000
# no output here
request.on "error", (e) =>
console.log "ERROR: #{e}"
# actually send the request
request.write params
request.end()
# run our function
run()
params
变量定义为:
params =
"arguments":
"filename": link
"method": "torrent-add"
"tag": 6667
在我设置有效的会话 ID 之前,一切正常。在第一次run
调用该函数时,我得到以下输出(对其进行了一些格式化以更方便眼睛):
状态:409
标题:
{ "server":"Transmission", "x-transmission-session-id":"io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR", "date":"Wed, 04 Apr 2012 08:37:37 GMT", "content-length":"580", "content-type":"text/html; charset=ISO-8859-1" }
sessionId:io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
身体:
409:冲突
您的请求包含无效的会话 ID 标头。
要解决此问题,请执行以下步骤:
- 读取响应时,获取其 X-Transmission-Session-Id 标头并记住它
- 将更新后的标头添加到您的传出请求中
- 当您收到此 409 错误消息时,请使用更新的标头重新发送您的请求
已添加此要求以帮助防止 CSRF 攻击。
X-Transmission-Session-Id: io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
当没有提供会话 ID 时,这正是远程服务器应该返回的内容。但是,在标头中设置会话 ID 后,服务器没有响应。第二次run
调用被触发并发送请求(通过放置一些有用console.log
的 s 来确认),但响应回调永远不会被触发。我没有收到来自远程服务器的响应,我的应用程序冻结等待。
我很确定错误在我这边,而不是在服务器上,因为在连接到同一个远程会话时,一个开箱即用的 android 远程客户端可以正常工作。
我是否正确执行了请求?特别是 JSON 部分?
编辑:一个小测试
我编写了一个小 php 脚本来测试 JSON 编码的请求是否正常,并将其用作“假”远程传输。这里是:
$headers = apache_request_headers();
// Simulate transmission's behavior
if (!isset($headers['X-Transmission-Session-Id'])) {
header("HTTP/1.0 409 Conflict");
header("X-Transmission-Session-Id: test");
}
print_r($headers);
// Is there a nicer way to get the raw request?
print_r(file_get_contents('php://input'));
而且,就我个人而言,我认为这次测试输出的数据没有任何问题。返回 409 状态代码后,Node.js 应用程序会为请求正确分配会话 ID。第一个print_r
打印一个数组:
Array
(
[Content-type] => application/json
[Content-length] => 152
[X-Transmission-Session-Id] => test
[Host] => tp.localhost
[Connection] => keep-alive
)
第二个打印一个字符串,它是一个格式正确的 JSON 字符串(没有更多内容):
{
"arguments": {
"filename": "http://link-to-torrent"
},
"method": "torrent-add",
"tag": 6667
}
我真的看不出我做错了什么。我用同一个远程服务器测试的一些第三方客户端可以正常工作。