M2Crypto BIO.readlines 挂起, python 2.7
我们正在把一个基于C++和OpenSSL的项目转换成使用Python和M2Crypto的版本,但在使用M2Crypto的BIO功能时遇到了一个比较奇怪的问题。具体来说,调用BIO.readlines()时会一直卡住,无法继续。
我们尝试了一个简单的例子:
f = open('test.txt','w')
f.write('hello world\n')
f.close()
import M2Crypto.BIO
bio = M2Crypto.BIO.openfile('test.txt','r')
lines = bio.readlines()
# the above call hangs forever
为了确认我们的OpenSSL安装没有什么大问题,我们写了一个小测试程序来读取我们刚创建的test.txt文件。
#include <openssl/bio.h>
#include <openssl/err.h>
int main() {
const int maxrd = 4096;
char line[maxrd];
int rd;
BIO* bio = BIO_new_file("test.txt","r");
while((rd = BIO_gets(bio, line, maxrd)) > 0) {
printf("%s",line);
}
if (rd == -1) {
printf("BIO error %ld\n", ERR_get_error());
}
}
结果没有问题。
我们研究了M2Crypto-0.21.1/SWIG/_bio.i这个包装文件,觉得可能找到了问题的根源。第109行测试了BIO_gets()的返回值。
if (r < 0) {
// return Py_None
}
但是,BIO_gets()的手册上说它可能返回0或-1来表示流的结束。
我认为应该是这样:
if (r < 1) {
// return Py_None
}
但我想看看其他人是否也遇到过这个问题,或者我们对BIO_gets()的理解是否有误。
--- 详细信息 --- Python 2.7 M2Crypto 0.21.1 OpenSSL 0.9.8q-fips 2010年12月2日 FreeBSD 8.2-RELEASE-p4
2 个回答
这个问题和Debian Linux中的一个错误有关,具体是错误 #717675。
在Fedora 21上这个问题无法重现,而且我没有找到任何会修改BIO.py
或_bio.i
的Fedora补丁。
下面是为Debian发布的补丁:
--- /usr/lib64/python2.7/site-packages/M2Crypto/BIO.py 2011-01-15 20:10:05.000000000 +0100
+++ BIO.py 2015-05-20 09:24:46.600582999 +0200
@@ -73,6 +73,8 @@
buf=m2.bio_gets(self.bio, 4096)
if buf is None:
break
+ if len(buf)==0:
+ break
lines.append(buf)
return lines
如果将来有人遇到这个问题,我想分享一下我们的修复方案。
--- M2Crypto-0.21.1.orig/SWIG/_bio.i 2011-01-15 14:10:06.000000000 -0500
+++ M2Crypto-0.21.1/SWIG/_bio.i 2012-02-14 11:34:15.000000000 -0500
@@ -106,7 +106,7 @@
Py_BEGIN_ALLOW_THREADS
r = BIO_gets(bio, buf, num);
Py_END_ALLOW_THREADS
- if (r < 0) {
+ if (r < 1) {
PyMem_Free(buf);
if (ERR_peek_error()) {
PyErr_SetString(_bio_err, ERR_reason_error_string(ERR_get_error()));
注意:对于那些了解M2Crypto内部工作原理的人来说,这个问题有三种解决方案。第一种就是上面提到的修复方案。我们认为这个方案符合BIO_gets()的手册说明,所以我们选择了这个方案。
第二种解决方案是修复M2Crypto/BIO.py。具体来说,就是修改实现BIO.readlines()的代码,检查m2.bio.gets()的返回值,如果是None或者缓冲区长度为0,就把这两种情况都当作流的结束。
第三种解决方案则是直接不调用BIO.readlines(),而只调用BIO.readline()(注意,单数的readline和复数的readlines是有区别的),并检查BIO.readline()的返回值是否为None或者缓冲区长度为0。
第三种方案看起来可能不太像是一个真正的解决办法,更像是一种规避。但如果你担心在一个可能没有修复M2Crypto的环境中部署应用程序,这种方法绝对是最兼容的。
我们已经把我们的修复方案提交给了负责的开发者Heikki,但他还没有机会审查我们的建议。在官方给出明确答复之前,我想先分享一下我们的想法。