О, чувак --- это одна из самых отягчающих и тонких ошибок, над которыми я когда-либо работал!У меня был такой соблазн просто прочитать XML-ответ в байтах, вернуть ByteArrayInputStream
и вернуть его, хотя я не знал, почему это решило проблему.Оказывается, это была моя ошибка, в некотором смысле технически, но все же ...
Так что получается, что если вы читаете контракт API на InputStream.read(byte b[], int off, int len)
, метод никогда не должен возвращать ноль байтов!Если он достигает конца данных, он должен возвращать -1 или блокировать, пока данные не станут доступны.(Что делать, если вызывающая сторона запрашивает ноль len
, неясно, так как это, по-видимому, не запрещено API. Более современный API будет определять, что IllegalArgumentException
должен быть выдан, если len<1
,но я отвлекся.)
My HTTPChunkedInputStream
автоматически анализирует фрагменты для получения фрагментированного HTTP-ответа.Как было написано, если бы вызывающий HTTPChunkedInputStream.read(byte b[], int off, int len)
запросил точно количество байтов, доступных в последнем чанке, то входной поток не будет активно пытаться загрузить дальнейшие чанки и распознать конец потока,Само по себе это не проблема, но в следующий раз следующий вызывающий запрос хочет больше байтов, способ, которым был написан алгоритм, мой входной поток попытался бы прочитать другой фрагмент, распознав, что больше не осталось фрагментов,а затем указывает, что нулевые байты были прочитаны !(Имейте в виду, это происходило только в том случае, если вызываемый сначала запрашивал точное количество байтов в последнем фрагменте, а затем позже запрашивал дополнительные байты.) В любое время после этого он будет возвращать -1, поскольку был достигнут конец данных.
Так что в этом конкретном случае по какой-то причине анализатор XML запросил ровно оставшиеся байты в ответе XML от WebDAV PROPFIND.Затем парсер захотел проверить, есть ли еще символы.Фактическое чтение происходит в UTF8Reader
;когда мой входной поток возвратил, что нулевые байты были прочитаны, это было передано XMLEntityScanner
.Ни один из этих классов не знает, как обрабатывать «не было прочитано ни одного байта» - просто предполагается, что что-то было прочитано.Наконец, XMLDocumentScannerImpl
проверяет, что это «что-то» было в строке 1453:
int ch = fEntityScanner.peekChar();
if (ch == -1) {
setScannerState(SCANNER_STATE_TERMINATED);
return XMLEvent.END_DOCUMENT ;
} else{
reportFatalError("ContentIllegalInTrailingMisc",
null);
fEntityScanner.scanChar();
setScannerState(SCANNER_STATE_TRAILING_MISC);
return XMLEvent.CHARACTERS;
}
Поскольку конец потока не был указан (он не знает, как обрабатывать «ничто»), он предполагал, что там было «что-то», и это что-то должно быть незаконным конечным контентом.
Вот так!Я исправил свой класс HTTPChunkedInputStream
, чтобы он никогда не возвращал нулевые байты из read()
.Я измотан - это одна из тех вещей, которые никогда не появляются, за исключением редких случаев при определенных условиях.И когда я прочитал байты и возвратил их в ByteArrayInputStream
, это не обнаружилось, потому что мой код для высвобождения байтов из HTTPChunkedInputStream
никогда не запрашивал точное количество байтов в последнем блоке --- и еслион все еще знал, как высосать эти нулевые байты и поместить их в буфер вместе с остальными.