Кодирование/декодирование заголовков HTTP в Java

Пользовательский HTTP-заголовок передается в приложение Servlet для целей аутентификации. Значение заголовка должно содержать акценты и другие символы, отличные от ASCII, поэтому они должны быть в определенной кодировке (в идеале UTF-8).

Мне предоставлен этот кусок кода Java разработчиками, которые контролируют среду проверки подлинности:

String firstName = request.getHeader("my-custom-header"); 
String decodedFirstName = new String(firstName.getBytes(),"UTF-8");

Но этот код мне не подходит: он предполагает кодировку значения заголовка, когда мне казалось, что существует правильный способ указать кодировку для значений заголовка (из MIME, я считаю).

Вот мой вопрос: какой правильный способ (tm) иметь дело с пользовательскими значениями заголовков, которые должны поддерживать кодировку UTF-8:

  • на проводе (как выглядит заголовок по кабелю)
  • с точки зрения декодирования (как его декодировать с использованием Java Servlet API, и можем ли мы предположить, что request.getHeader() уже правильно декодирует)

Вот пример кода, независимого от среды, для обработки заголовков как UTF-8, если вы не можете изменить свою службу:

String valueAsISO = request.getHeader("my-custom-header"); 
String valueAsUTF8 = new String(firstName.getBytes("ISO8859-1"),"UTF-8");

Ответ 1

Опять же: RFC 2047 не реализован на практике. Следующая версия HTTP/1.1 собирается удалить упоминание об этом.

Итак, если вам нужно переносить символы, отличные от ASCII, самым безопасным способом является их кодирование в последовательность ASCII, например заголовок "Slug" в Протоколе публикации Atom.

Ответ 2

Рабочая группа HTTPbis знает об этой проблеме, а последние черновики избавляются от всего языка в отношении кодировки TEXT и RFC 2047 - он не используется на практике по протоколу HTTP.

См. http://trac.tools.ietf.org/wg/httpbis/trac/ticket/74 для всей истории.

Ответ 3

Как уже упоминалось, первый взгляд всегда должен перейти к HTTP 1.1 spec (RFC 2616). Он говорит, что текст в значениях заголовка должен использовать MIME-кодировку как определенную RFC 2047, если он содержит символы из наборов символов, отличных от ISO-8859-1.

Итак, вот плюс для вас. Если ваши требования охвачены кодировкой ISO-8859-1, вы просто помещаете своих персонажей в свои сообщения о запросе/ответе. В противном случае MIME-кодирование является единственной альтернативой.

Пока пользовательский агент отправляет значения в ваши пользовательские заголовки в соответствии с этими правилами, вам не придется беспокоиться об их расшифровке. Это то, что должен делать Servlet API.


Однако есть более основополагающая причина, по которой ваш sniplet кода не выполняет то, что он должен. Первая строка извлекает значение заголовка в виде строки Java. Как мы знаем, он представлен как UTF8 внутренне, поэтому на этом этапе выполняется разбор и обработка сообщений HTTP-запроса.

Следующая строка выбирает массив байтов этой строки. Поскольку кодировка не указана (IMHO этот метод без аргумента должен был устаревать давно), используется текущая системная кодировка по умолчанию, которая обычно не является UTF8, а затем массив снова преобразуется в кодировку UTF8. Outch.

Ответ 4

См. спецификацию HTTP для правил, которая гласит в разделе 2.2

Правило TEXT используется только для описательного содержимого поля и значений, которые не предназначены для интерпретации анализатором сообщений. Слова * ТЕКСТ МОЖЕТ содержать символы из наборов символов, отличных от ISO-8859-1 [22], только при кодировании в соответствии с правилами RFC 2047 [14].

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

Ответ 5

Спасибо за ответы. Похоже, что идеальным было бы следовать правильному кодированию заголовка HTTP в соответствии с RFC 2047. Значения заголовка в UTF-8 на проводе выглядели бы примерно так:

=?UTF-8?Q?...?=

Теперь вот смешная вещь: кажется, что ни Tomcat 5.5, ни 6 правильно декодируют HTTP-заголовки в соответствии с RFC 2047! Код Tomcat предполагает, что значения заголовков используют ISO-8859-1.

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