httplib
(少なくとも Python 2.7 では)の動作はContent-Length
、独自のヘッダーを追加する前に既存のヘッダーがあるかどうかを確認するため、コンテンツのサイズがわかっている場合は、独自のヘッダーを追加できます。たとえば、次のようになります。
c.request("POST", "/xpost", s, headers={"Content-Length": len(s.getvalue())})
そのようなヘッダーがない場合は、自動的に 1 つを埋めるためhttplib
に呼び出しを試みlen()
ます。これが失敗した場合は、本体がファイルのようなオブジェクトであると想定os.fstat()
し、OS レベルのファイル記述子を呼び出してそのサイズを決定します。指定したファイルハンドルでメソッドを呼び出しfileno()
ます。これは実際のファイルでは問題なく機能しますが、StringIO
オブジェクトは実際のファイルではないため、fileno()
メソッドを提供せず、操作はAttributeError
. このエラーは によってキャッチされ、暗黙のうちに処理されhttplib
ます。これは単にContent-Length
.
間違いなくオブジェクトを使用している場合、上記の例で示したように、独自のヘッダーStringIO
を追加するのがおそらく最も簡単なオプションです。これが作業中の単なるテストであり、実際に実際のファイルを使用する場合は、プラットフォームで機能する限り、ヘッダーを正しく設定するContent-Length
ことができます。そうでない場合は、いつでもファイル名で自分自身を呼び出し、同じ方法で独自のヘッダーを指定できます。httplib
os.fstat()
os.stat()
両方の実際のファイルを処理したい場合StringIO
は、いつでも次のようにすることができます。
headers = {}
if not hasattr(body, "fileno"):
headers["Content-Length"] = len(body.getvalue())
...しかし、必要でない限り、その複雑さを追加することはお勧めしません。
最後に、HTTP レベルでは、チャンク エンコーディングを使用する別のオプションがあります。この場合、ヘッダーを指定する必要はありませんContent-Length
。本文自体は、データの自己記述チャンクでエンコードされます。残念ながら、クライアントとサーバー (httplib
含まれている)の両方の多くの HTTP ソフトウェアは、応答のみがチャンクされ、要求は常にContent-Length
. この仮定は、リクエストが通常小さいためだと思いますが、もちろんPOST
、PUT
この仮定は成立しません。
サーバーがチャンク化されたリクエストを処理できると確信している場合は、それを試すことができます-そうするには、チャンク化されたエンコーディングが既に含まれているオブジェクト(またはの自動挿入を無効にする方法がStringIO
ないもの)を構築する必要があります配置し、独自のヘッダーに value を指定します。個人的には、ソフトウェアがさまざまなサーバーで動作することを目指している場合、これはお勧めしません。fileno()
httplib
Content-Length
Transfer-Encoding
chunked
編集:余談ですが、チャンク エンコーディングを使用する場合は、ヘッダーを送信してはなりません。応答を受信する接続がありません。Content-Length
チャンクされたリクエストのサポートがいかに貧弱であるかの例として、nginxは昨年末にバージョン 1.3.9でそれをコア機能に追加しただけです (ただし、その前にプラグインがありました)。
編集2:
ウィキペディアの記事を読むと、単に正しいヘッダーを送信するだけでなく、もう少し多くの作業があることがわかります。本体をチャンクに分割し、チャンクのサイズ (16 進数) で構成される小さなヘッダーを付けてそれぞれを送信する必要があります。これは通常、応答を送信するときに行われますが、前述したように、要求でのサポートは不十分です。
本文をチャンクに変換する、ファイルのようなオブジェクトのラッパーの例を次に示します。上記の例を使用して使用方法を示しましたが、もちろん"hello world"
本体は非常に小さいため、最終的には単一のチャンクになります。ただし、どのサイズのボディでも機能するはずです。read()
Python オブジェクトと同じように機能するメソッドを持つ任意のオブジェクトで機能するはずfile
です。実際、標準の Python ファイル オブジェクトをこれらのいずれかでラップすると、orがサポートされていないため、httplib
追加できなくなります。Content-Length
len()
fileno()
Transfer-Encoding
以下の例に示すように、ヘッダーを自分で追加することを忘れないでください。
import httplib
import cStringIO
class ChunkedEncodingWrapper(object):
def __init__(self, fileobj, blocksize=8192):
self.fileobj = fileobj
self.blocksize = blocksize
self.current_chunk = ""
self.closed = False
def read(self, size=None):
ret = ""
while size is None or size >= len(self.current_chunk):
ret += self.current_chunk
if size is not None:
size -= len(self.current_chunk)
if self.closed:
self.current_chunk = ""
break
self._get_chunk()
else:
ret += self.current_chunk[:size]
self.current_chunk = self.current_chunk[size:]
return ret
def _get_chunk(self):
if not self.closed:
chunk = self.fileobj.read(self.blocksize)
if chunk:
self.current_chunk = "%x" % (len(chunk),) + "\r\n" + chunk + "\r\n"
else:
self.current_chunk = "0\r\n\r\n"
self.closed = True
s = cStringIO.StringIO("hello world")
w = ChunkedEncodingWrapper(s)
c = httplib.HTTPConnection("xxx.xxx.xxx.xxx")
c.request("POST", "/xpost", w, headers={"Transfer-Encoding": "chunked"})