Недавно я расширил API Python для API хранилища Windows Azure (PyAzure), включив поддержку API управления службами. См. https://github.com/bmb/pyazure .
Я использую HTTPSClientAuthHandler, подобный предложенному в , используя pyOpenSSL для создания настраиваемого средства открытия urllib . В Linux с различными версиями Python 2.6 и 2.7 это хорошо работает. Однако Windows - это совсем другое дело. Все запросы к адресу хоста управления Azure завершаются ошибкой:
[Errno 10054] Существующее соединение было принудительно закрыто удаленным хостом
Я думаю, это сокет errno 10054 "Connection reset by peer", перетаскиваемый.
Похоже, это не проблема в моем коде API (если только используемый мной метод аутентификации сертификата клиента каким-то образом не является фиктивным), это что-то низкоуровневое. Я могу воспроизвести проблему без urllib2 или httplib, просто настроив сокет SSL и отправив тот же HTTP-запрос по каналу, что и urllib2, например чтобы перечислить допустимые местоположения центров обработки данных Azure:
>>> import socket, ssl, sys
>>> sys.version
'2.7.1 (r271:86832, Nov 27 2010, 17:19:03) [MSC v.1500 64 bit (AMD64)]'
>>> s = ssl.wrap_socket(socket.socket(), certfile='c:\\users\\blair\\research\\clouds\\azure\\BlairBethwaiteAzure1.pfx.pem')
>>> s.connect(('management.core.windows.net',443))
>>> s.send("GET /SUBSCRIPTION_ID/locations HTTP/1.1\r\nAccept-Encoding: identity\r\nX-Ms-Version: 2011-10-01\r\nHost: management.core.windows.net\r\nConnection: close\r\nUser-Agent: Python-urllib/2.6\r\n\r\n")
202
>>> s.read()
Traceback (most recent call last):
c:\Users\blair\research\clouds\azure\pyazure\
in ()
----> 1 s.read()
C:\Python27\lib\ssl.pyc in read(self, len)
136
137 try:
--> 138 return self._sslobj.read(len)
139 except SSLError, x:
140 if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
error: [Errno 10054] An existing connection was forcibly closed by the remote host
Замените SUBSCRIPTION_ID выше на свой идентификатор подписки Azure. Исключение возникает через ~ 45 секунд после вызова SSLSocket.read. Сертификат представляет собой правильно отформатированный файл PEM, включающий как закрытый ключ, так и сертификат, он был преобразован из файла pfx (в Ubuntu 10.04) используя:
openssl pkcs12 -in pfxfile -out pemfile -nodes
Я не думаю, что здесь это имеет значение, но я также пробовал использовать unix2dos-файл PEM, но безрезультатно. У меня такое же поведение, даже когда я не предоставляю сертификат, но выполнение этого в Linux приводит к правильной ошибке API с сервера:
'HTTP / 1.1 403 Forbidden \ r \ nContent-Length: 0 \ r \ nСервер: Microsoft-HTTPAPI / 2.0 \ r \ nДата: 1 декабря 2011 г., 13:59:29 GMT \ r \ nСоединение: закрыть \ r \ n \ r \ n '
Это было независимо проверено другим человеком, использующим Windows 7 (такая же, как у меня). Это не проблема брандмауэра на стороне клиента - тот же код работает в виртуальной машине Linux с NAT, работающей на том же хосте.
Я в тупике. Был бы очень признателен за любую помощь, которую могли бы оказать здесь люди ...
Обновление: Похоже, это связано с базовой реализацией SSL в Python. В CPython 2.7.1 возникает ошибка, как показано выше, но с тех пор я успешно протестировал ActiveState Python (как 2.7, так и 2.6), например:
>>> import sys, socket, ssl
>>> sys.version
'2.7.1 (r271:86832, Feb 7 2011, 11:30:38) [MSC v.1500 32 bit (Intel)]'
>>> s = ssl.wrap_socket(socket.socket(), certfile='\\\\VBOXSVR\\azure\\BlairBethwaiteAzure1.pfx.pem')
>>> s.connect(('management.core.windows.net',443))
>>> s.send('GET /SUBSCRIPTION_ID/locations HTTP/1.1\r\nAccept-Encoding: identity\r\nX-Ms-Version: 2011-10-01\r\nHost: management.core.windows.net\r\nUser-Agent: Python-urllib/2.6\r\n\r\n')
183
>>> s.read(4096)
'HTTP/1.1 200 OK\r\nContent-Length: 908\r\nContent-Type: application/xml; charset=utf-8\r\nServer: Microsoft-HTTPAPI/2.0\r\nx-ms-request-id: 08ca048cda6b445da6b3a8f3e4890197\r\nDate: Fri, 02 Dec 2011 03:02:14 GMT\r\n\r\nAnywhere US Anywhere US South Central US South Central US North Central US North Central US Anywhere Europe Anywhere Europe North Europe North Europe West Europe West Europe Anywhere Asia Anywhere Asia Southeast Asia Southeast Asia East Asia East Asia '
И, как и ожидалось, мой API тоже работает:
ActivePython 2.6.7.20 (ActiveState Software Inc.) based on
Python 2.6.7 (r267:88850, Jun 27 2011, 13:20:48) [MSC v.1500 64 bit (AMD64)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from pyazure import pyazure
>>> pa = pyazure.PyAzure(subscription_id=SUBSCRIPTION_ID, management_cert_path='c:\\users\\blair\\research\\clouds\\azure\\BlairBethwaiteAzure1.pfx.pem')
>>> list(pa.wasm.list_locations())
['Anywhere US', 'South Central US', 'North Central US', 'Anywhere Europe', 'North Europe', 'West Europe', 'Anywhere Asia', 'Southeast Asia', 'East Asia']
The Lib \ Файлы ssl.py в CPython2.7 и ActivePython2.7 идентичны, поэтому я предполагаю, что это должно быть связано с некоторой разницей в базовых библиотеках C, возможно, с ошибкой в CPython. Есть какие-нибудь гуру?