2010-12-13 3 views
9

Я пытаюсь написать код python, который может создавать множественные HTTP-запросы в клиенте, а затем соответствующим образом интерпретировать его на сервере. У меня есть, я думаю, частично удалось на стороне клиента с этим:Создайте и проанализируйте многостраничные HTTP-запросы в Python

from email.mime.multipart import MIMEMultipart, MIMEBase 
import httplib 
h1 = httplib.HTTPConnection('localhost:8080') 
msg = MIMEMultipart() 
fp = open('myfile.zip', 'rb') 
base = MIMEBase("application", "octet-stream") 
base.set_payload(fp.read()) 
msg.attach(base) 
h1.request("POST", "http://localhost:8080/server", msg.as_string()) 

Единственная проблема состоит в том, что библиотека электронной почты также включает Content-Type и заголовки MIME-Version, и я не уверен, как они будут связаны с HTTP-заголовков, включенных HTTPLIB:

Content-Type: multipart/mixed; boundary="===============2050792481==" 
MIME-Version: 1.0 

--===============2050792481== 
Content-Type: application/octet-stream 
MIME-Version: 1.0 

Это может быть причиной того, что, когда этот запрос получен мой web.py приложения, я просто получаю сообщение об ошибке. Обработчик web.py POST:

class MultipartServer: 
    def POST(self, collection): 
     print web.input() 

Выдает эту ошибку:

Traceback (most recent call last): 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 242, in process 
    return self.handle() 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 233, in handle 
    return self._delegate(fn, self.fvars, args) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 415, in _delegate 
    return handle_class(cls) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 390, in handle_class 
    return tocall(*args) 
    File "/home/richard/Development/server/webservice.py", line 31, in POST 
    print web.input() 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/webapi.py", line 279, in input 
    return storify(out, *requireds, **defaults) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 150, in storify 
    value = getvalue(value) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 139, in getvalue 
    return unicodify(x) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 130, in unicodify 
    if _unicode and isinstance(s, str): return safeunicode(s) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 326, in safeunicode 
    return obj.decode(encoding) 
    File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) 
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 137-138: invalid data 

Моя строка кода представлена ​​линией ошибки примерно на половине пути вниз:

File "/home/richard/Development/server/webservice.py", line 31, in POST 
    print web.input() 

Это поставляемая вместе , но я не уверен, куда идти отсюда. Является ли это проблемой с моим клиентским кодом или ограничением web.py (возможно, он просто не поддерживает многопроцессорные запросы)? Любые подсказки или предложения альтернативных библиотек кода были бы с благодарностью получены.

РЕДАКТИРОВАТЬ

Ошибка выше было вызвано данными не автоматически в кодировке base64. Добавление

encoders.encode_base64(base) 

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

<Storage {'Content-Type: multipart/mixed': u'', 
      ' boundary': u'"===============1342637378=="\n' 
      'MIME-Version: 1.0\n\n--===============1342637378==\n' 
      'Content-Type: application/octet-stream\n' 
      'MIME-Version: 1.0\n' 
      'Content-Transfer-Encoding: base64\n' 
      '\n0fINCs PBk1jAAAAAAAAA.... etc 

Так что-то не так там.

Благодаря

Richard

+0

Какая черта - это многопользовательский HTTP-запрос? Действительно ли эта концепция используется? – SingleNegationElimination

+0

@TokenMacGuy - да. да. –

ответ

1

После небольшого исследования ответ на этот вопрос стал ясен. Короткий ответ заключается в том, что хотя Content-Disposition is optional в MIME-кодированном сообщении, web.py требует его для каждой части mime, чтобы правильно разобрать HTTP-запрос.

Вопреки другим комментариям по этому вопросу разница между HTTP и Email не имеет значения, поскольку они являются просто транспортными механизмами для сообщения Mime и ничего более. Многостраничные/связанные (не multipart/form-data) сообщения являются общими при обмене содержимым веб-сервисов, который здесь используется. Однако предоставленные фрагменты кода точны, и привели меня к немного более кратким решению проблемы.

# open an HTTP connection 
h1 = httplib.HTTPConnection('localhost:8080') 

# create a mime multipart message of type multipart/related 
msg = MIMEMultipart("related") 

# create a mime-part containing a zip file, with a Content-Disposition header 
# on the section 
fp = open('file.zip', 'rb') 
base = MIMEBase("application", "zip") 
base['Content-Disposition'] = 'file; name="package"; filename="file.zip"' 
base.set_payload(fp.read()) 
encoders.encode_base64(base) 
msg.attach(base) 

# Here's a rubbish bit: chomp through the header rows, until hitting a newline on 
# its own, and read each string on the way as an HTTP header, and reading the rest 
# of the message into a new variable 
header_mode = True 
headers = {} 
body = [] 
for line in msg.as_string().splitlines(True): 
    if line == "\n" and header_mode == True: 
     header_mode = False 
    if header_mode: 
     (key, value) = line.split(":", 1) 
     headers[key.strip()] = value.strip() 
    else: 
     body.append(line) 
body = "".join(body) 

# do the request, with the separated headers and body 
h1.request("POST", "http://localhost:8080/server", body, headers) 

Это подобрано отлично хорошо web.py, так что ясно, что email.mime.multipart подходит для создания сообщений Mime для транспортировки по протоколу HTTP, за исключением его обработки заголовка.

Моя общая общая уверенность в масштабируемости. Ни это решение, ни другие, предложенные здесь, не очень хорошо масштабируются, поскольку они считывают содержимое файла в переменную перед объединением в сообщение mime. Лучшим решением было бы такое, которое могло бы сериализоваться по требованию, поскольку контент передается по HTTP-соединению. Мне не срочно это исправлять, но я вернусь сюда с решением, если я доберусь до него.

+0

1. Я думаю, что предпочтительным способом установки заголовка является нечто вроде 'base.add_header ('Content-Disposition', 'file', name = 'package', .. .) '. 2. Лучше искать '\ n \ n' (а также' \ r \ n \ r \ n', например, 're.search ('\ r? \ N \ r? \ N', ...) '), поэтому вам не нужно разделять и присоединяться к телу. 3. Линии заголовка могут быть свернуты. 4. Технически «\ n», который завершает заголовки, не является частью тела, хотя это не является вредным. 5. Я не совсем уверен, что грамматики RFC 5322 и RFC 2316 на 100% совместимы (в частности, символы WRT «против октетов»). –

0

Существует целый ряд вещей, не так с вашим запросом. Как указывает TokenMacGuy, multipart/mixed не используется в HTTP; вместо этого используйте multipart/form-data. Кроме того, части должны иметь заголовок Content-disposition. Фрагмент python для этого можно найти в Code Recipes.

+0

спасибо - как видите, я все еще работаю над этим; Я еще не выяснил, где еще установлено множество/смешанных. Аналогично, я еще не использовал заголовок Content-Disposition, так как я все еще работаю над тем, чтобы он попал в HTTP-запрос. Мой вопрос заключается в том, как создать такой запрос в первую очередь. Cheers, R. –

+0

См. Рецепт. Забудьте о email.mime - HTTP не является электронной почтой. –

+0

Привет, Мартин; FYI. Я продемонстрировал, что разница между HTTP и электронной почтой здесь не имеет значения - они просто переносятся, а mime в обоих случаях одинакова. См. Мой альтернативный ответ. Спасибо за указатели. R –

1

Я использовал этот пакет Will Holcomb http://pypi.python.org/pypi/MultipartPostHandler/0.1.0, чтобы делать запросы с несколькими частями с urllib2, это может помочь вам.

+0

Отлично, спасибо, я возьму посмотри на это. Конечно, имеет правильное название: Cheers, R –