BaseHTTPRequestHandler: многократный анализ запросов и проблемы длины контента - PullRequest
0 голосов
/ 18 ноября 2018

У меня проблемы с анализом необработанной строки HTTP-запроса и попыткой подтвердить вычисленное значение длины содержимого.

Проблема была обнаружена при анализе запроса POST, содержащего многокомпонентные данные. В этом случае значение заголовка Content-length отличается от того, которое я вычисляю с помощью len (rfile.read). Я предполагаю, что это должно быть как-то связано с кодировкой символов в двоичном контенте, но я не нашел способа получить тот же результат, что и тот, который предоставляется HTTP-заголовком Content-length.

Следующий скрипт демонстрирует проблему:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from BaseHTTPServer import BaseHTTPRequestHandler
from StringIO import StringIO

str_http = """POST /abc HTTP/1.1
Host: 127.0.0.1:80
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.18.4
content-type: text/plain
Content-Length: 7

abc 123
"""

str2_http = """POST /spi/v2/events HTTP/1.1
User-Agent: Crashlytics Android SDK/1.3.8.127
X-CRASHLYTICS-DEVELOPER-TOKEN: XXXXXXXXXXX
X-CRASHLYTICS-API-CLIENT-TYPE: android
X-CRASHLYTICS-API-CLIENT-VERSION: 1.3.8.127
X-CRASHLYTICS-API-KEY: XXXXXXXXXXX
Content-Type: multipart/form-data; boundary=00content0boundary00
Host: e.crashlytics.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 776

--00content0boundary00
Content-Disposition: form-data; name="session_analytics_file_0"; filename="sa_32e4d6c3-adef-4cd5-a571-a68e4bee65f6_1542460750651.tap"
Content-Type: application/vnd.crashlytics.android.events

�Ko�0dz�p��@Z�\��3~do
                       �B-Uw˩�v�ռ�Gi��͡��.�r@�U�
b�R43{��i,����'~���σ`o�)Tu��/Mnߘpꪈ�j�U}e�D�4M�L��+���U�:�ƌ�

                                                               D����b� &�-5U����T��{]�R���,(K%K@�lS��{�f�ux�ʁ��`�;w���f�(}���R������[����ﴠ9�U� � К"I�A��I*  ���^վ�M���᳃XĒ`�]�^:m+cs��ˋ��X����._���uӬj�Wr�|]o�{�e�   ~`>R�`��G�
�QQ�3��19� e�s]����d�ΥS.oÙ���ܥ�U���s՞G��6�ζ�rm�������nB��[Cp^�'^{�CnGvI�w�����p�H�#;HÄ�Z��������=����c5�����$۶  ��%��c@i�U,
                                                                                                                                           �p����������G�
                                                                                                                                                                s�����8�����aC
--00content0boundary00--
"""


class HTTPRequest(BaseHTTPRequestHandler):
    def __init__(self, request_text):
        self.rfile = StringIO(request_text)
        # ~ self.rfile = io.BytesIO(request_text)

        self.raw_requestline = self.rfile.readline()
        self.error_code = self.error_message = None
        self.parse_request()
        self.request_text = request_text

    def send_error(self, code, message):
        self.error_code = code
        self.error_message = message



# 1st test : OK
request = HTTPRequest(str_http)
content_length = int(request.headers.get('content-length') or 0)
data_cursor = request.rfile.tell()

print request.rfile.read(content_length)                    # Complete body

request.rfile.seek(data_cursor)
print len(request.rfile.read().strip()) == content_length   # True


# 2nd test : KO
request = HTTPRequest(str2_http)
content_length = int(request.headers.get('content-length') or 0)
data_cursor = request.rfile.tell()

print request.rfile.read(content_length)                    # Truncated body

request.rfile.seek(data_cursor)
print len(request.rfile.read().strip()) == content_length   # False

Знаете ли вы, как правильно рассчитать это значение? Моя цель - иметь возможность рассчитать правильную длину данных, чтобы точно знать, когда запрос завершил доставку своего контента.

1 Ответ

0 голосов
/ 26 ноября 2018

Если другие люди застряли на подобной проблеме, мне наконец удалось выяснить, как заставить это работать. Как и предполагалось в моем предыдущем посте, все сводится к кодированию: символы не-ASCII должны быть закодированы в шестнадцатеричном формате (например, \ xff).

Следующий код показывает рабочий пример с проблемной строкой:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from BaseHTTPServer import BaseHTTPRequestHandler
from StringIO import StringIO

str_http= """POST /spi/v2/events HTTP/1.1\r\nUser-Agent: Crashlytics Android SDK/1.3.8.127\r\nX-CRASHLYTICS-DEVELOPER-TOKEN: XXXXXX\r\nX-CRASHLYTICS-API-CLIENT-TYPE: android\r\nX-CRASHLYTICS-API-CLIENT-VERSION: 1.3.8.127\r\nX-CRASHLYTICS-API-KEY: XXXXXX\r\nContent-Type: multipart/form-data; boundary=00content0boundary00\r\nHost: e.crashlytics.com\r\nConnection: Keep-Alive\r\nAccept-Encoding: gzip\r\nContent-Length: 642\r\n\r\n--00content0boundary00\r\nContent-Disposition: form-data; name="session_analytics_file_0"; filename="sa_986e8438-14b5-44a5-a02e-6c7b1cce9b84_1543176379199.tap"\r\nContent-Type: application/vnd.crashlytics.android.events\r\n\r\n\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed\x91KN\xe40\x14E\x0bV\xc0\x12J\x19\x13\xe3$\xcf\x8eS\xb3\x82\xeeA\x0b\xd1\x03\xaa\xc4\x88\xc9\xf3\'`\xe18Q>|\x84\xd8{;\xa9\xa2%&,\x00\xf9H\x9e\xbc{\xaf}m\xafVg\xab\xc0IXg\x87uz\xf2\x9e`\xd7]N^;\xf3G\'\x9bD\xb5\rQ\xd8\xf7\xa6n\xa7\x9e\xd4V\x13\xf4\xbao\xadN\xce\x13\xf3j\xd44\xda\xd6/\xce\x1a\x18\x17\x14u\xca\xa8\x82\x14P\xb1T\x16\x08\xa9\x00\x9es\xceX-\x14\x84\x90\xf5\xc3\x88\xce\xe1\xff\\%\xf3L\x96\x92i*\x18\xa0\xc9\x05\xab\xa0\x94\x14*j$\x18\xceC\xe6x\xe4b/h\t:\xe7\x90C\t\xc0\xc1\x04\xd9\xd9\xc6\x8e[\xbd\xefQ=Y\xff\xf0\xdb\xa3t&xkt\x839O\xe4d\xdd!\x0b\x8a\xb3\x02\xeb*E\x1a\x8a\x81Ry*r\xa1\xd3Z\xf1J\x8a,\xcb5\xcb\xc2~\xedpg\xfa!\xf4\x0b\x11N\xe8\xfdEQ\x16"\xa3"H\xda<[enZm\\\x10\'\xff\xe4\xdb\x17\x7f\x7f\xb1=\x14\\\xef~]\xaf\xe7\xe3\xc6u\xdd\xf6\xebW\xb1\x94\xef\xba\xe3vW!6\xdf\xa0\xa4_\xc6\x7f\xb1\x99\xc7\x82P2\x0b\xa3mLx\xa2\xa6K6\x19\x83"+yQV\x19\x88\xa0\xbcu\xb3q\xb7\xdf\xde\xee\x97.#Z7$\x9b\xf0gj\xb4\xcfv|\xfb\xee\xc3\xc8\xd09\x1c\x1e\xc9d\xc9\xd1n\xcd@v\xcbp\xfb\x99\xff\xf8XE"\x91H$\x12\x89D"\x91H\xe4\xa7\xf1\x0f\xcd}\xfe\x17\x00\x10\x00\x00\r\n--00content0boundary00--\r\n"""

class HTTPRequest(BaseHTTPRequestHandler):
    def __init__(self, request_text):
        self.rfile = StringIO(request_text)
        self.raw_requestline = self.rfile.readline()
        self.error_code = self.error_message = None
        self.parse_request()
        self.request_text = request_text

    def send_error(self, code, message):
        self.error_code = code
        self.error_message = message


request = HTTPRequest(str_http)
content_length = int(request.headers.get('content-length') or 0)


data_cursor = request.rfile.tell()

print request.rfile.read(content_length)                    # Complete body

request.rfile.seek(data_cursor)
print len(request.rfile.read()) == content_length           # True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...