0


Rest API を呼び出すアクティブなコード ジェネレーター用の Python 言語プラグインを作成しています。requests ライブラリの使用を何度も試みて失敗した後、はるかに低いレベルのソケットと ssl モジュールを使用することにしました。これまでのところ正常に動作しています。私は応答を解析するために非常に大雑把な方法を使用しています。本文のかなり短い応答の場合、これは正常に機能しますが、現在、はるかに大きな json オブジェクト (ユーザーのリスト) を取得しようとしています。応答は次のように途切れています (注: 簡潔にするためにいくつかのユーザー エントリを削除しました):
{"page-start":1,"total":5,"userlist":[{"userid":"jim.morrison","first-name":"Jim","last-name":"Morrison","language":"English","timezone":"(GMT+5:30)CHENNAI,KOLKATA,MUMBAI,NEW DELHI","currency":"US DOLLAR","roles":
この後、さらに数人のユーザーが表示され、応答本文はコンソールの 1 行に表示されます。

Rest APIサーバーからユーザーリストをリクエストするために使用しているコードは次のとおりです。

import socket, ssl, json

host = self.WrmlClientSession.api_host
port = 8443
pem_file = "<pem file>"

url = self.WrmlClientSession.buildURI(host, port, '<root path>')

#Create the header
http_header = 'GET {0} HTTP/1.1\n\n'
req = http_header.format(url)

#Socket configuration and connection execution
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn = ssl.wrap_socket(sock, ca_certs = pem_file)
conn.connect((host, port))
conn.send(req)

response = conn.recv()
(headers, body) = response.split("\r\n\r\n")

#Here I would convert the body into a json object, but because the response is 
#cut off, it cannot be properly decoded.  
print(response) 

この問題についての洞察をいただければ幸いです。

編集:サーバー側で応答をデバッグしたことを忘れていましたが、すべてが完全に正常でした。

4

1 に答える 1

3

recv()TCP 接続は限られた量しかバッファリングしないため、1 回呼び出すだけですべてのデータを取得できるとは限りません。また、期待する本文のサイズを決定するためにヘッダーを解析していません。ノンブロッキング ソケットを使用して、ブロックされるまで読み続けることができます。これはほとんどの場合機能しますが、信頼性が低く、実践が非常に貧弱であるため、ここでは説明しません。

まさにこの理由で、HTTP には本文のサイズを示す方法があり、コードを信頼できるものにしたい場合は、それらを使用するのが正しい方法です。探すべきことが 2 つあります。まず、HTTP 応答にContent-Lengththen が含まれている場合、それは応答本文で発生するバイト数を示します。その量になるまで読み続ける必要があります。2 番目のオプションは、サーバーがチャンク エンコーディングを使用する応答を送信する可能性があることです。これはTransfer-Encoding、値にテキストが含まれるヘッダーを含めることによってこれを示しますchunked。ここではチャンク エンコーディングには触れません。ウィキペディアの記事を読んでください。詳細については。基本的に、本文には、データの「チャンク」ごとに、そのチャンクのサイズを示す小さなヘッダーが含まれています。この場合、応答の終わりを示す空のチャンクを取得するまで、チャンクを読み続ける必要があります。Content-Lengthこのアプローチは、サーバーが送信を開始したときに、サーバーが応答本文のサイズを認識していない場合の代わりに使用されます。

通常、サーバーはContent-Lengthと チャンク エンコーディングの両方を使用しませんが、実際にそれを停止するものは何もないため、これも考慮する必要があります。特定のサーバーとのみ相互運用する必要がある場合は、それが何をするかを伝えてそれを操作するだけで済みますが、コードの移植性が低下し、将来の変更に対して脆弱になることに注意してください。

これらのヘッダーを使用する場合、特定の読み取り操作が不完全なデータを返す可能性があるため、ループで読み取る必要があることに注意してください。TCP は、読み取りアプリケーションがバッファーを空にし始めるまでデータの送信を停止するように設計されているため、そうではありません。回避できるもの。また、各読み取りには完全なチャンクが含まれていない可能性があるため、現在のチャンクのサイズと既に見たチャンクの量について状態を追跡する必要があることに注意してください。前のチャンク ヘッダーで指定されたバイト数を確認した場合にのみ、次のチャンク ヘッダーを読み取る必要があります。

もちろん、Python の無数の HTTP ライブラリのいずれかを使用する場合は、これについて心配する必要はありません。以前にかなり完全な HTTP/1.1 クライアントを実装しなければならなかった人として言えば、可能であれば他の人にそれを任せたいと思っています.ケースが多い。requestsうまくいかない場合は、標準の Python ライブラリを試しましたか? 高レベルのインターフェースにはurllibとがあり、問題の一部を回避できる低レベルのアプローチを提供します。urllib2httplib

問題を本当に修正する必要がある場合は、これらのコードをいつでも変更できることを忘れないでください (もちろん、ローカル リポジトリにコピーした後)。または、問題をインポートして変更をモンキー パッチするだけです。明確にする必要があります。ただし、ライブラリの問題であり、誤った使用だけではありません。

本当に HTTP クライアントを実装したい場合は問題ありませんが、見た目よりも難しいことに注意してください。

最後に、私は常にread()代わりに SSL ソケットの方法を使用してきました。recv()それらが同等であることを願っていますが、まだ問題がある場合は、それを試してください。

于 2013-03-09T10:13:32.220 に答える