一個同事,使用jira的REST api對jira進行修改,用python的httplib發請求,發現發出POST請求后,服務器總是返回json缺少close marker。
於是自己搞了個nginx,把post請求的內容打出來,發現后面的確少了2個字符,很奇怪,開始懷疑httplib對content-len的計算有問題,於是在httplib中加了一些print打印中間結果。把請求頭和請求+body的字符串msg都打印出來
def _send_output(self, message_body=None): """Send the currently buffered request and clear the buffer. Appends an extra \\r\\n to the buffer. A message_body may be specified, to be appended to the request. """ self._buffer.extend(("", "")) msg = "\r\n".join(self._buffer) print msg del self._buffer[:] # If msg and message_body are sent in a single send() call, # it will avoid performance problems caused by the interaction # between delayed ack and the Nagle algorithim. if isinstance(message_body, str): msg += message_body message_body = None print msg self.send(msg) if message_body is not None: #message_body was not a string (i.e. it is a file) and #we must run the risk of Nagle self.send(message_body)
結果發現header跟body之間多了一個換行符('\r\n'),http協議默認header和body之間有一個空行隔開,也就是一個只含有\r\n的行,但是多了一個\r\n,就會導致服務器取body的時候從這個多出來的\r\n開始取content-length個字符,這樣body里最后的兩個字符就被這個多出來的\r\n擠掉了
而通過觀察,這個原因是由於header的最后一個字段Authorization: Basic eHh4eHh4eDp4eHh4eHh4后面多了一個"\n"導致,
這個字段的值是同事經過base64.encodestring("XXXXXX:XXXXXX")編碼后得到的字符串,查看了一下python的lib doc,發現這個函數默認返回一個以"\n"結尾的字符串,這就這個問題的根本原因,replace掉其中的\n,一切就都OK了
base64.encodestring返回的字符串默認結尾帶"\n",而且產生的base64編碼字符串每76個字符就會用"\n"隔開,所以最安全的方法不是strip去掉結尾的\n,而是用replace去掉其中所有的\n。為啥base64.ecodestring每76字符就換行,這個是mime協議的規定,用於email發送,具體查看mime協議吧