1

まあ、それはかなりあいまいなトピックですが、試してみます。誰かが答えを知っているかもしれません。

Transmission BitTorrent Client 用の小さなリモート 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"
}

セッション ID: io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR

体:

409:コンフリクト

リクエストに無効なセッション ID ヘッダーが含まれていました。

これを修正するには、次の手順に従います。

  1. 応答を読み取るときに、その X-Transmission-Session-Id ヘッダーを取得して記憶します
  2. 更新されたヘッダーを発信リクエストに追加します
  3. この 409 エラー メッセージが表示されたら、ヘッダーを更新してリクエストを再送信してください

この要件は、CSRF 攻撃を防ぐために追加されました。

X-Transmission-Session-Id: io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR

これは、セッション ID が指定されていない場合にリモート サーバーから返されるものです。ただし、ヘッダーにセッション ID を設定した後、サーバーは応答しません。2 番目のrun呼び出しが発生し、リクエストが送信されます (いくつかconsole.logの有用な を配置することによって確認されます) が、応答コールバックは決して発生しません。リモート サーバーから応答がなく、アプリケーションがフリーズして待機します。

Android用のすぐに使用できるリモートクライアントは、同じリモートセッションに接続するときに問題なく動作するため、エラーはサーバーではなく自分の側にあると確信しています。

リクエストを正しく実行していますか? 特にJSON部分?

編集:ちょっとしたテスト

JSON でエンコードされたリクエストが正常かどうかをテストするための小さな php スクリプトを作成し、それを「偽の」リモート送信として使用しました。ここにあります:

$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
)

2 番目のものは、適切にフォーマットされた JSON 文字列である文字列を出力します (その中には何もありません)。

{
  "arguments": {
    "filename": "http://link-to-torrent"
  },
  "method": "torrent-add",
  "tag": 6667
}

何が間違っているのか本当にわかりません。同じリモート サーバーでテストした一部のサードパーティ クライアントは、正常に動作します。

4

3 に答える 3

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()
于 2012-04-30T16:22:33.180 に答える
0
#!/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
于 2012-04-09T12:01:49.927 に答える
0

さて、私は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

「生の」バージョンの何が問題だったのかまだわからないため、当面はこの回答を受け入れないままにします。

于 2012-04-05T14:09:42.133 に答える