【问题标题】:Remote Transmission session doesn't respond after providing corrent session id提供正确的会话 ID 后,远程传输会话没有响应
【发布时间】:2012-04-17 23:49:35
【问题描述】:

嗯,这将是一个相当晦涩的话题,但我会尝试一下,也许有人会知道答案。

我正在为传输 BitTorrent 客户端编写一个小的远程 Node.js 客户端。使用 JSON 对象通过 RPC 处理通信。

这里是specification

还有我的代码(用 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"
}

会话ID: io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR

正文:

409: 冲突

您的请求包含无效的会话 ID 标题。

要解决此问题,请按以下步骤操作:

  1. 读取 响应,获取其 X-Transmission-Session-Id 标头并记住它
  2. 将更新后的标头添加到您的传出请求中
  3. 当你得到这个 409 错误消息,重新发送您的请求并更新 标题

已添加此要求以帮助防止 CSRF 攻击。

X-Transmission-Session-Id: io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR

当没有提供会话 ID 时,这正是远程服务器应该返回的内容。但是,在标头中设置会话 ID 后,服务器没有响应。第二个run 调用被触发并发送请求(通过放置一些有用的console.logs 来确认),但响应回调从未触发。我没有收到来自远程服务器的响应,我的应用程序冻结等待。

我很确定错误是在我这边,而不是在服务器那边,因为在连接到同一个远程会话时,一个开箱即用的 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
}

我真的看不出我做错了什么。我用同一个远程服务器测试的一些第三方客户端可以正常工作。

【问题讨论】:

    标签: json http node.js transmission


    【解决方案1】:

    我上过这门课时遇到了同样的问题。我在想更好的方法来做一个 getData, 方法。但它有效。

    http = require "http"
    _ = require "underscore"
    
    class Connect
      constructor: (@login, @password, @host='127.0.0.1', @port=9091, @headers={}) ->
    
      getData: (params)->
        key = "x-transmission-session-id"
    
        options = {
          host: @host
          port: @port
          path: '/transmission/rpc',
          method: 'POST',
          headers: @headers || {},
          auth: "#{ @login }:#{ @password }"  
        }
    
        _.extend options, params || {}
    
        req = http.request(options, (res)=>
          if res.statusCode == 401
            console.log "Auth errror"
    
          else if res.statusCode == 409
            auth_header={}
            auth_header[key] = res.headers[key]
    
            _.extend @headers, auth_header
            @getData(params)
    
          else if res.statusCode == 200
            res.setEncoding 'utf8'
            res.on('data', (chunk)->
              #here should be an emmit of data
              console.log chunk
            )
    
          else
            console.log "Error #{ res.statusCode }"
    
        )
        req.write('data\n')
        req.write('data\n')
        req.end()
    
    connector = new Connect "transmission", "password"
    
    connector.getData()
    

    【讨论】:

      【解决方案2】:

      好吧,我能够使用mikeal's request 规避 - 但不能解决 - 问题,这也简化了我的代码。最新版本如下所示:

      runRemoteCommand: (params, callback = false) =>
        options =
          uri: @uri
          method: "POST"
          json: params
      
        if @sessionId
          options.headers = 
            "X-Transmission-Session-Id": @sessionId
      
        request options, (error, response, body) =>
          retVal = 
            success: false
          end = true
      
          if error
            retVal.message = "An error occured: #{error}"
          else
            switch response.statusCode
              when 409
                if response.headers["x-transmission-session-id"]
                  @sessionId = response.headers["x-transmission-session-id"]
                  end = false
                  @.runRemoteCommand params, callback
                else
                  retVal.message = "Session id not present"
              when 200
                retVal.success = true
                retVal.response = body
              else retVal.message = "Error, code: #{response.statusCode}"
      
           callback retVal if end && callback
      

      我暂时不接受这个答案,因为我仍然不知道“原始”版本有什么问题。

      【讨论】:

        【解决方案3】:
        #!/bin/bash
        
        #-----------------------------------------------------------------------
        #
        
        DEBUG=0
        HOST="192.168.1.65"
        PORT="8181"
        TRURL="http://$HOST:$PORT/transmission/rpc"
        USER="admin"
        PASSWORD="password1"
        XTSID=""
        
        
        #-------------------------------------
        # 
        function getSID ()
        {
            local S="$1"
            S=${S##*X-Transmission-Session-Id: }
            S=${S%%</code>*}
            echo $S
        }
        
        #-------------------------------------
        function getData ()
        {
            local REQUEST="$1"
        
            local RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
                -H "Content-type: application/json" \
                -X POST \
                -d "$REQUEST" \
                --user $USER:$PASSWORD $TRURL)
        
            ((DEBUG)) && echo $XTSID
        
            if [[ "$RET" =~ "409: Conflict" ]] 
            then
                XTSID=$(getSID "$RET")
        
                ((DEBUG)) && echo "XTSID $XTSID"
        
                RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
                    -H "Content-type: application/json" \
                    -X POST \
                    -d "$REQUEST" \
                    --user $USER:$PASSWORD $TRURL)
            fi
        
            echo $RET
        }
        
        #-------------------------------------
        
        R='{"method":"session-stats"}'
        RET=$(getData "$R")
        echo $RET
        

        【讨论】:

        • 我真的不知道这对我有什么用处。我在使用标准库在 Node.js 中编写客户端时遇到问题,而不是在编写客户端时。我已经编写了另外两个供参考,一个在 php 中,一个在 Node.js 中,使用 mikeal 的请求库,它们运行良好。我的问题是我不知道是什么让这个特定的代码冻结而不是接收来自远程传输的响应。
        猜你喜欢
        • 2021-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-01
        • 2012-07-23
        • 2015-03-10
        • 1970-01-01
        • 2012-06-12
        相关资源
        最近更新 更多