HTTP multipart / form-data.Что происходит, когда двоичные данные не имеют строкового представления? - PullRequest
0 голосов
/ 26 апреля 2018

Я хочу написать реализацию HTTP.

Я искал несколько дней об отправке файлов по HTTP с помощью Content-Type: multipart/form-data, и мне действительно интереснокак браузеры (или любой HTTP-клиент) создают такие запросы.

Я уже рассмотрел множество вопросов по этому поводу здесь в stackoverflow, например:
Какработает загрузка файла HTTP?
Что означает enctype = 'multipart / form-data'?

Я копаю RFC 2616 (и более новые версии), 2046,и т. д. Но я не нашел четкого ответа (очевидно, у меня не было идеи за ним).

В большинстве статей и ответов я нашел этот фрагмент строки запроса, который мне просто интерпретировать,все это задокументировано в RFC ...

POST /upload?upload_progress_id=12344 HTTP/1.1
Host: localhost:3000
Content-Length: 1325
Origin: http://localhost:3000
... other headers ...
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryePkpFF7tjBAqx29L

------WebKitFormBoundaryePkpFF7tjBAqx29L
Content-Disposition: form-data; name="MAX_FILE_SIZE"

100000
------WebKitFormBoundaryePkpFF7tjBAqx29L
Content-Disposition: form-data; name="uploadedfile"; filename="hello.o"
Content-Type: application/x-object

... contents of file goes here ...
------WebKitFormBoundaryePkpFF7tjBAqx29L--

... и было бы просто реализовать HTTP-клиент для создания фрагмента строки таким способом на любом языке.

Проблема становится на ... contents of file goes here ..., мало информации о том, что "содержимое файла" является.Я знаю, что это двоичные данные с определенным типом и кодировкой, но из строковых данных сложно придумать, как бы я добавил часть двоичных данных, которые не имеют строкового представления внутри строки.

Я хотел бы увидеть примеры низкоуровневых реализаций протокола HTTP с любым языком.И, возможно, подробные объяснения о передаче двоичных данных по HTTP, как клиент создает запросы и как сервер читает / анализирует их.

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

1 Ответ

0 голосов
/ 26 апреля 2018

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

Итакособенно не применяется кодировка, нет utf-8, нет base64, HTTP не является протоколом с ограничением ascii7, таким как smtp, где применяется кодировка base64 для обеспечения использования только символов ascii7.

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

Именно поэтому большинство HTTP-серверов используют C для управления HTTP, они анализируютHTTP-байт на байт (поскольку заголовки протокола являются только ascii 7, определенно не многобайтовыми символами), и они также могут легко читать / записывать произвольные двоичные данные для тела (или даже использовать системные вызовы, такие как readfile дляпусть ядро ​​управляет двоичной частью).

Теперь о примерах .

Когда выse Content-Length и без составных элементов тело имеет длину (длина содержимого) в байтах, поэтому клиент, анализирующий ваши отправленные данные, просто прочитает это количество байтов и будет обрабатывать все эти необработанные данные как телоконтент (который может иметь тип MIME и информацию о кодировке, но это просто информация для слоев, установленных поверх протокола HTTP).

При использовании Transfer-Encoding: chunked ,Необработанное двоичное тело разделяется на части, каждая часть затем имеет префикс шестнадцатеричного числа (размер фрагмента) и маркер конца строки.С окончательным нулевым маркером в конце.

Если мы возьмем пример википедии :

4\r\n
Wiki\r\n
5\r\n
pedia\r\n
E\r\n
 in\r\n
\r\n
chunks.\r\n
0\r\n
\r\n

Мы могли бы заменить каждую букву ascii7 любым байтом, даже байтомэто не имело бы представления ascii7, я буду использовать символ * для каждого байта реального тела:

4\r\n
****\r\n
5\r\n
*****\r\n
E\r\n
**************\r\n
0\r\n
\r\n

Все остальные символы являются частью протокола HTTP (здесь передача фрагментированного тела).Я также мог бы использовать \n представление двоичных данных и отправлять только нулевой байт для каждого байта тела, это было бы:

4\r\n
\0\0\0\0\0\r\n
5\r\n
\0\0\0\0\0\0\r\n
E\r\n
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\r\n
0\r\n
\r\n

Это просто представление, мы также могли бы использовать \xNN или \NN представления, в действительности это байты, 8 бит (слишком ленив, чтобы написать представление 0/1 этого тела :-)).

Если текст примера, вместо того, чтобы быть:

Wikipedia in\r\n
\r\n
chunks.

Он мог бы быть более сложным, с многобайтовыми символами (здесь é в utf-8):

Wikipédia in\r\n
\r\n
chunks.

Этот é фактически 11000011:10101001 в utf-8, два байта: \xc3\xa9 в \xNN представлении) вместо простого символа 01100101 / \x65 / e.Тело HTTP теперь (см., Что размер второго блока равен 6, а не 5):

4\r\n
Wiki\r\n
6\r\n
p\xc3\xa9dia\r\n
E\r\n
 in\r\n
\r\n
chunks.\r\n
0\r\n
\r\n

Но это допустимо только в том случае, если исходные данные были эффективно в utf-8, возможно, была другой кодировкой.По умолчанию, если на вашем веб-сервере нет определенных параметров конфигурации, где вы применяете преобразование исходного документа в определенную кодировку, на самом деле веб-сервер не выполняет преобразование исходного документа, вы берете то, что имеете,и вы можете добавить заголовок, чтобы сообщить клиенту, какая кодировка была определена в исходном документе.

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

...