2013-07-02 1 views
1

코드 : 요청이()에 의해 전송 된 데이터) (수 렌되지 않은 s을 유발하지만, s의 내용을 보내, 그래서해야하므로request()의 param 'body'가 len() 가능하지 않으면 httplib에서 본문을 전송하지 않습니까? 이 같은

import httplib 
import cStringIO 

s = cStringIO.StringIO("hello world") 
c = httplib.HTTPConnection("xxx.xxx.xxx.xxx") 
c.request("POST", "/xpost", s) 

s는 읽기() 할 객체입니다 request()에 Content-Length 헤더가없고 body 영역에 본문 내용이 없습니다.

그래서 body 길이를 보낼 수 없는데도 POST를 보낼 수 있습니까?

답변

3

httplib (적어도 Python 2.7 이상)의 동작은 적어도 하나의 자체를 추가하기 전에 기존 Content-Length 헤더가 있는지 확인하는 것과 같으므로 내용의 크기를 알면 우연히 추가 할 수 있습니다. 자신의 헤더 - 예를 들어 : 거기에 자동으로 일을 채우기 위해 len()를 호출하는 그런 헤더, httplib 시도가 없으며,이 실패 할 경우는 몸이 파일 - 류의 객체 여야 가정에 os.fstat()를 호출하면

c.request("POST", "/xpost", s, headers={"Content-Length": len(s.getvalue())}) 

OS 레벨의 파일 디스크립터로 그 크기를 결정한다. 파일 핸들에서 fileno() 메서드를 호출하여이 디스크립터를 얻는다. 그것을 줘. 이것은 실제 파일에서는 잘 작동하지만 StringIO 오브젝트는 실제 파일이 아니기 때문에 fileno() 메소드를 제공하지 않으며 조작은 AttributeError과 함께 실패합니다. 이 오류는 httplib에 의해 캐치되어 자동으로 처리되며 이는 단순히 Content-Length을 추가하지 못합니다.

StringIO 개체를 확실히 사용하는 경우 위의 예에서 설명한 것처럼 가장 쉬운 방법은 직접 Content-Length 헤더를 추가하는 것입니다. 실제 상황에서 실제 파일을 사용할 때 테스트하는 것일 경우 이 플랫폼에서 작동하는 한 httplib을 사용하여 헤더를 올바르게 설정할 수 있습니다. 그렇지 않은 경우 파일 이름에 항상 os.stat()으로 전화하고 같은 방식으로 고유 헤더를 제공 할 수 있습니다. 당신이 진짜 파일과 StringIO 모두 처리하려면

당신은 항상 같은 것을 수행 할 수 있습니다

headers = {} 
if not hasattr(body, "fileno"): 
    headers["Content-Length"] = len(body.getvalue()) 

을 ...하지만 난 당신이 그것을 필요로하지 않는 당신이 그 복잡성을 추가하지 않는 것이 좋습니다.

마지막으로 HTTP 레벨에는 chunked encoding을 사용하는 또 다른 옵션이 있는데 여기서는 Content-Length 헤더를 제공 할 필요가 없으며 본문 자체는 자체 설명 덩어리로 인코딩됩니다. 그러나 불행하게도 많은 클라이언트와 서버 (httplib 포함)의 HTTP 소프트웨어는 응답이이고 청취 대상이 이고은 항상 Content-Length을 사용한다고 가정하는 경향이 있습니다. 이 가정은 요청은 일반적으로 작기 때문에 가정합니다. 물론 POSTPUT으로 가정하면 물을 보유하지 않습니다. 당신이 당신의 서버가 청크 요청을 처리 할 것으로 확신 가정

, 당신은 그것을 시도 할 수 - 그렇게, 당신이 httplib의 자동 Content-Length을 물리 칠 수있는 StringIO 객체 (또는 fileno() 방법으로 무엇을 구축해야합니다 삽입)을 삽입하고 자신의 Transfer-Encoding 헤더에 값 chunked을 제공하십시오.개인적으로 나는 당신이 다양한 서버로 작업 할 때 당신의 소프트웨어를 목표로한다면 이것을 추천하지 않을 것이다.

편집 : 여담으로, 당신을 인코딩 청크 사용하는 경우는 Content-Length 헤더를 보내지해야한다 - 요청으로 HTTP RFC §4.4 항목 3을 참조 물론, 당신은 몸의 말을 신호 수 없습니다 응답을 수신 할 연결이 없기 때문에 연결을 닫는 것만으로도 충분합니다.

청크 요청의 빈약 한 지원의 예로서 nginx은 작년 말에 version 1.3.9의 핵심 기능에만 추가했습니다 (그 전에는 a plugin for it 이었지만).

편집 2 :

당신이 위키 백과 문서를 읽으면 당신이 그것에 좀 더 간단하게 올바른 헤더를 보내는 것보다 거기에 볼 수 있습니다 - 당신이 덩어리로 몸을 분할하고 각 하나를 보낼 수있다 작은 헤더는 16 진수의 청크 크기로 구성됩니다. 이것은 일반적으로 응답을 보낼 때 당신을 위해 행해지 겠지만 요청에 대한 지원이 좋지 않다고 언급했습니다.

다음은 본문을 청크로 변환하는 파일과 같은 객체를 둘러싼 래퍼의 예입니다. "hello world" 몸체가 너무 작아서 단 하나의 덩어리로 끝나기는하지만, 위의 예를 적용하여 사용 방법을 보여 줬습니다. 그러나 그것은 어떤 크기의 몸에도 효과가 있습니다. read() 메서드를 사용하는 모든 객체에서 작동해야합니다.이 객체는 Python file 객체와 동일한 방식으로 작동합니다. 실제로 이들 중 하나에 표준 파이썬 파일 객체를 래핑하면 httpliblen() 또는 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"}) 
+0

감사와 같은 긴 응답 : 대한 많은,하지만, 난 여전히 청크 데이터를 전송하는 방법을 알아낼 수 없습니다 'httplib'에 의해 몸체가 unlen() 가능한 스트림이기 때문에 몸체의 길이는 사용할 수 없습니다. 나는 헤더에'Transfer-Encoding : chunked'를 추가하려하지만 여전히 chunkded body를 보내지 못한다 : ( – Coaku

+0

'Transfer-Encoding' 헤더를 추가하는 것 이상을해야한다 - 스트림 주위에 넣을 수있는 래퍼를 작성하면'httplib'가 소비 할 수있는 형식으로 청크 인코딩 된 데이터를 방출합니다. – Cartroo

관련 문제