Почему поле заголовка HTTP Content-Length использует значение, отличное от заданного в коде Java?

#java #http #http-headers #http-content-length

#java #http #http-заголовки #http-content-length

Вопрос:

У меня есть фрагмент Java-кода для передачи массива байтов на HTTP-сервер:

 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary="
      myBoundary);
connection.setRequestProperty("Content-Length", 1024);
  

Я использовал этот код для передачи массива байтов, размер которого превышает 1024. Это сработало хорошо. Но фактическое HTTP-сообщение (захваченное Wireshark) показывает, что значение Content-Length соответствует фактическому размеру вместо 1024. Почему?

Я искал в спецификации HTTP, но не нашел подсказки. Я не использовал никакого кодирования передачи или Transfer-coding.

Ответ №1:

Я бы предположил, что HttpURLConnection просто переопределит Content-Length заголовок правильным значением, поскольку он знает, что лгать об этом нехорошо 😉

И действительно: в строках 535-550 из sun.net.www.protocol.HttpURLConnection the Content-Length устанавливается, если это уместно. Это происходит после установки пользовательских заголовков, так что это значение будет перезаписано.

И это правильно: если объем передаваемых вами данных не соответствует заявленному объему, то вы только запутаете другой конец.

Проверяя источник sun.net.www.protocol.http.HttpURLConnection , кажется, что существует список заголовков, которые ограничены и будут автоматически игнорироваться при вызове setRequestProperty . Content-Length входит в этот список. К сожалению, это, похоже, недокументировано (по крайней мере, я не смог найти никакой документации по этому поводу, только обсуждение связанной проблемы здесь).

Поиск в Google идентификаторов ошибок (?), упомянутых в наборе изменений, который ввел эту «функциональность», похоже, что это изменение было введено как реакция на уязвимости в системе безопасности CVE-2010-3541 и CVE-2010-3573 (ошибка Redhat в этой теме).

Ограничение можно отключить вручную, установив для системного свойства sun.net.http.allowRestrictedHeaders значение true при запуске JVM.

Комментарии:

1. Значение Content-Length было жестко задано по ошибке. Что меня удивляет, так это то, что это работает. Я хочу знать почему.

2. Ваше предположение звучит разумно. Но где найти документацию по нему?

3. @joachim-sauer: Отличный ответ! Но я протестировал с sun.net.http.allowRestrictedHeaders= true, и обнаружил, что результат такой же, как и раньше.

4. @wang: мой подробный анализ здесь на самом деле неприменим, поскольку Content-Length в любом случае будет перезаписано правильное значение. Я обновил свой ответ.

Ответ №2:

Это решило для меня:

 connection.setFixedLengthStreamingMode(myString.getBytes().length);
conn.setRequestProperty("Content-length", String.valueOf(myString.getBytes().length));
  

«connection.setFixedLengthStreamingMode(myString.getBytes().length);» перед установкой заголовка Content-Length.