URLConnection неправильно обрабатывает длину контента через прокси - PullRequest
5 голосов
/ 07 марта 2012

Я столкнулся со следующей проблемой: Когда через прокси используется URLConnection, длина контента всегда устанавливается на -1.

Сначала я проверил, что прокси действительно возвращает Content-Length (lynx и wget также работают через прокси; другого выхода в интернет из локальной сети нет):

$ lynx -source -head ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
HTTP/1.1 200 OK
Last-Modified: Mon, 09 Jul 2007 17:02:37 GMT
Content-Type: application/x-zip-compressed
Content-Length: 30745
Connection: close
Date: Thu, 02 Feb 2012 17:18:52 GMT

$ wget -S -X HEAD ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
--2012-04-03 19:36:54--  ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
Resolving proxy... 10.10.0.12
Connecting to proxy|10.10.0.12|:8080... connected.
Proxy request sent, awaiting response...
  HTTP/1.1 200 OK
  Last-Modified: Mon, 09 Jul 2007 17:02:37 GMT
  Content-Type: application/x-zip-compressed
  Content-Length: 30745
  Connection: close
  Age: 0
  Date: Tue, 03 Apr 2012 17:36:54 GMT
Length: 30745 (30K) [application/x-zip-compressed]
Saving to: `WO2003-104476-001.zip'

В Java я написал:

URL url = new URL("ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip");
int length = url.openConnection().getContentLength();
logger.debug("Got length: " + length);

и я получаю -1. Я начал отлаживать FtpURLConnection, и оказалось, что необходимая информация находится в базовом поле HttpURLConnection.responses, однако никогда не заполняется должным образом:

enter image description here (в заголовках Content-Length: 30745). Длина содержимого не обновляется, когда вы начинаете читать поток или даже после того, как поток был прочитан. Код:

URL url = new URL("ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip");
URLConnection connection = url.openConnection();

logger.debug("Got length (1): " + connection.getContentLength());

InputStream input = connection.getInputStream();

byte[] buffer = new byte[4096];
int count = 0, len;
while ((len = input.read(buffer)) > 0) {
    count += len;
}

logger.debug("Got length (2): " + connection.getContentLength() + " but wanted " + count);

Выход:

Got length (1): -1
Got length (2): -1 but wanted 30745

Кажется, что это ошибка в JDK6, поэтому я открыл новую ошибку # 7168608 .

  • Если кто-то может помочь мне написать код, он должен вернуть правильную длину содержимого для прямого FTP-соединения, FTP-соединения через прокси-сервер и локальных file:/ URL-адресов.
  • Если данную проблему нельзя обойти с помощью JDK6, предложите любую другую библиотеку, которая определенно работает для всех упомянутых мной случаев ( Apache Http Client ?).

Ответы [ 3 ]

2 голосов
/ 02 апреля 2012

Помните, что прокси часто изменяют представление базовой сущности. В вашем случае я подозреваю, что прокси, вероятно, изменяет кодировку передачи. Что, в свою очередь, делает Content-Length бессмысленным, даже если он указан.

Вы попали в следующие два раздела спецификации HTTP 1.1:

4.4 Длина сообщения

  1. ...
  2. ...
  3. Если присутствует поле заголовка Content-Length (раздел 14.13), его десятичное значение в OCTET представляет как длину объекта, так и длину передачи. Поле заголовка Content-Length НЕ ДОЛЖНО отправляться, если эти две длины различны (то есть, если присутствует поле заголовка Transfer-Encoding). Если сообщение получено как с полем заголовка Transfer-Encoding, так и с полем заголовка Content-Length, последнее ДОЛЖНО игнорироваться.

14.41 Передача-кодировка

Поле общего заголовка Transfer-Encoding указывает, какой (если есть) тип преобразования был применен к телу сообщения для безопасной передачи его между отправителем и получателем. Это отличается от кодирования содержимого тем, что кодирование передачи является свойством сообщения, а не объекта.

Transfer-Encoding       = "Transfer-Encoding" ":" 1#transfer-coding

Кодировки перевода определены в разделе 3.6. Пример:

Transfer-Encoding: chunked

Если к объекту применено несколько кодировок, кодировки передачи ДОЛЖНЫ быть перечислены в том порядке, в котором они были применены. Дополнительная информация о параметрах кодирования МОЖЕТ предоставляться другими полями заголовка объекта, не определенными в данной спецификации.

Многие старые приложения HTTP / 1.0 не понимают заголовок Transfer-Encoding.

Таким образом, URLConnection игнорирует заголовок Content-Length согласно спецификации, потому что он бессмыслен при наличии chunked передач

На скриншоте отладчика неясно, присутствует ли заголовок Transfer-Encoding. Пожалуйста, дайте нам знать ...

При дальнейшем расследовании - похоже, что lynx не показывает все заголовки, возвращаемые при вводе lynx -head. Он не показывает заголовок Transfer-Encoding, критичный для этого обсуждения.

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

Ξ▶ lynx -useragent='dummy' -source -head http://www.bbc.co.uk                                                                                                                  
HTTP/1.1 302 Found
Server: Apache
X-Cache-Action: PASS (non-cacheable)
X-Cache-Age: 0
Content-Type: text/html; charset=iso-8859-1
Date: Tue, 03 Apr 2012 13:33:06 GMT
Location: http://www.bbc.co.uk/mobile/
Connection: close

Ξ▶ wget -useragent='dummy' -S -X HEAD http://www.bbc.co.uk                                                                                                                 
--2012-04-03 14:33:22--  http://www.bbc.co.uk/
Resolving www.bbc.co.uk... 212.58.244.70
Connecting to www.bbc.co.uk|212.58.244.70|:80... connected.
HTTP request sent, awaiting response... 
HTTP/1.1 200 OK
Server: Apache
Cache-Control: private, max-age=15
Etag: "7e0f292b2e5e4c33cac1bc033779813b"
Content-Type: text/html
Transfer-Encoding: chunked
Date: Tue, 03 Apr 2012 13:33:22 GMT
Connection: keep-alive
X-Cache-Action: MISS
X-Cache-Age: 0
X-LB-NoCache: true
Vary: Cookie

Поскольку я явно не в вашей сети, я не могу воспроизвести ваши точные обстоятельства, но, пожалуйста, подтвердите, что вы действительно не получаете заголовок Transfer-Encoding при прохождении через прокси.

1 голос
/ 03 апреля 2012

Я думаю, что это «ошибка» в jdk, связанная с обработкой FTP-соединений, которые проксируются. FtpURLConnection делегирует HttpURLConnection, когда используется прокси. однако , FtpURLConnection, по-видимому, не делегирует никакого управления заголовками этому HttpURLConnection в этой ситуации. Таким образом, вы можете правильно получать потоки, но я не думаю, что вы можете получить доступ к любым значениям "заголовка", таким как длина контента или тип контента. (это основано на быстром взгляде на исходный код openjdk для 1.6, я мог что-то упустить).

0 голосов
/ 28 марта 2012

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

URLConnection connection= url.openConnection();
InputStream input= connection.getInputStream();
byte[] buffer= new byte[4096];
while(input.read(buffer) > 0)
  ;
logger.debug("Got length: " + getContentLength());

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

...