2013-03-08 1 views
0


Я пишу плагин языка python для активного генератора кода, который вызывает вызовы в наш Rest API. Сделав много попыток использовать библиотеку запросов и failing, я решил использовать гораздо более низкоуровневые сокеты и модули ssl, которые до сих пор работали нормально. Я использую очень грубый метод для анализа ответов; для довольно коротких ответов в теле это прекрасно работает, но теперь я пытаюсь получить гораздо большие объекты json (списки пользователей). Ответ прерывается следующим образом (примечание: для краткости я удалил пару пользовательских записей):
{"page-start":1,"total":5,"userlist":[{"userid":"jim.morrison","first-name":"Jim","last-name":"Morrison","language":"English","timezone":"(GMT+5:30)CHENNAI,KOLKATA,MUMBAI,NEW DELHI","currency":"US DOLLAR","roles":
После этого должно быть еще несколько пользователей, а тело ответа находится на одной линии в консоли ,Что может привести к отключению корпуса ответа (на стороне клиента)?

Вот код, я использую, чтобы запросить список пользователей с сервера API Rest:

import socket, ssl, json 

host = self.WrmlClientSession.api_host 
port = 8443 
pem_file = "<pem file>" 

url = self.WrmlClientSession.buildURI(host, port, '<root path>') 

#Create the header 
http_header = 'GET {0} HTTP/1.1\n\n' 
req = http_header.format(url) 

#Socket configuration and connection execution 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
conn = ssl.wrap_socket(sock, ca_certs = pem_file) 
conn.connect((host, port)) 
conn.send(req) 

response = conn.recv() 
(headers, body) = response.split("\r\n\r\n") 

#Here I would convert the body into a json object, but because the response is 
#cut off, it cannot be properly decoded. 
print(response) 

Любое понимание в этом вопросе будет весьма признателен!

Редактировать: Я забыл упомянуть, что отлаживал ответ на серверной стороне, и все было совершенно нормально.

ответ

1

Вы не можете предположить, что вы можете просто позвонить recv() один раз и получить все данные, поскольку TCP-соединение будет буферизовать только ограниченную сумму. Кроме того, вы не разбираете ни один из заголовков, чтобы определить размер тела, которого вы ожидаете. You может использовать неблокирующий сокет и продолжать читать до тех пор, пока он не блокирует, что будет работать в основном, но просто ненадежна и довольно плохая практика, поэтому я не буду документировать его здесь.

HTTP имеет способы указания размера тела именно по этой причине, и правильный подход заключается в их использовании, если вы хотите, чтобы ваш код был надежным. Есть две вещи, которые нужно искать. Во-первых, если ответ HTTP имеет значение , то это указывает, сколько байтов будет присутствовать в теле ответа - вам нужно продолжать чтение, пока у вас это не получилось. Второй вариант заключается в том, что сервер может отправить вам ответ, который использует chunked encoding - он указывает на это, включив заголовок Transfer-Encoding, значение которого будет содержать текст chunked. Я не буду вдаваться в кодировку chunked здесь, прочитайте wikipedia article для деталей. По сути, тело содержит небольшие заголовки для каждого «куска» данных, которые указывают размер этого фрагмента. В этом случае вы должны продолжать читать куски, пока не получите пустую, что указывает на конец ответа. Этот подход используется вместо Content-Length, когда размер тела ответа не известен серверу, когда он начинает его отправлять.

Как правило, сервер не будет использовать как , так и закодированную кодировку, но нет ничего, что могло бы фактически остановить его, так что это тоже необходимо рассмотреть. Если вам нужно только взаимодействовать с конкретным сервером, тогда вы можете просто сказать, что он делает, и работать с этим, но помните, что вы сделаете свой код менее портативным и более хрупким для будущих изменений.

Обратите внимание, что при использовании этих заголовков вам все равно нужно читать в цикле, потому что любая заданная операция чтения может возвращать неполные данные. TCP предназначен для прекращения отправки данных до тех пор, пока приложение чтения не начнет очищать буфер, поэтому это не то, что вы можете обойти. Также обратите внимание, что каждый прочитанный может даже не содержать полный фрагмент, поэтому вам нужно отслеживать состояние размера текущего фрагмента и того количества, которое вы уже видели. Вы знаете только, чтобы прочитать следующий заголовок блока, когда вы видите количество байтов, указанное предыдущим заголовком блока.

Конечно, вам не о чем беспокоиться, если вы используете огромное количество библиотек HTTP от Python. Говоря как кто-то, кто раньше должен был выполнить достаточно полный HTTP/1.1-клиент, вы действительно хотите, чтобы кто-то другой сделал это, если возможно, - есть достаточно много сложных случайных ситуаций, и ваш простой код выше будет терпеть неудачу в много случаев. Если requests не подходит для вас, попробовали ли вы какие-либо стандартные библиотеки Python? Для интерфейсов более высокого уровня есть urllib и urllib2, а httplib обеспечивает подход более низкого уровня, который вы можете найти, позволяющий обойти некоторые из ваших проблем.

Помните, что вы всегда можете изменить код в них (после копирования в ваш локальный репозиторий, конечно), если вам действительно нужно исправить проблемы или, возможно, просто импортировать их и обезвредить ваши изменения. было совершенно ясно, что это проблема в библиотеке, а не просто ошибочное ее использование.

Если вы действительно хотите реализовать HTTP-клиент, это прекрасно, но просто имейте в виду, что это сложнее, чем кажется.

В конце концов, я всегда использовал метод сокетов SSL read() вместо recv(). Надеюсь, они были бы эквивалентны, но вы можете попробовать, если у вас все еще есть проблемы.

Смежные вопросы