5

我遇到了以下问题:URLConnection通过代理使用时,内容长度始终设置为-1.

首先,我检查了代理是否真的返回Content-Lengthlynx并且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字段中,但它从未从那里正确填充:

在此处输入图像描述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 中的一个错误,所以我打开了新的bug#7168608

  • 如果有人可以帮助我编写代码,应该返回正确的内容长度以用于直接 FTP 连接、通过代理的 FTP 连接和本地file:/URL,我将不胜感激。
  • 如果给定的问题不能用 JDK6 解决,建议任何其他绝对适用于我提到的所有情况的库(Apache Http Client?)。
4

3 回答 3

2

请记住,代理通常会更改基础实体的表示。在您的情况下,我怀疑代理可能正在更改传输编码。这反过来使 Content-Length 即使提供也毫无意义。

您违反了 HTTP 1.1 规范的以下两个部分:

4.4 消息长度

  1. ...
  2. ...
  3. 如果存在 Content-Length 头字段(第 14.13 节),则其在 OCTET 中的十进制值表示实体长度和传输长度。如果这两个长度不同(即,如果存在 Transfer-Encoding 头字段),则不得发送 Content-Length 头字段。如果接收到的消息同时带有 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 标头。

因此Content-Length,根据规范,URLConnection 然后忽略标头,因为在存在分块传输的情况下它是没有意义的

在您的调试器屏幕截图中,不清楚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 标头。

于 2012-04-02T04:18:27.970 回答
1

我认为这是 jdk 中与处理代理的 ftp 连接相关的“错误”。当使用代理时,FtpURLConnection 委托给 HttpURLConnection。 但是,在这种情况下,FtpURLConnection 似乎没有将任何标头管理委托给此 HttpURLConnection。因此,您可以正确获取流,但我认为您不能访问任何“标题”值,例如内容长度或内容类型。(这是基于对 1.6 的 openjdk 源代码的快速浏览,我可能错过了一些东西)。

于 2012-04-03T18:17:23.967 回答
0

检查我会做的一件事是实际阅读响应(写下我的头顶,所以期待错误):

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

如果你得到的大小很好,那么寻找一种方法让 URLConnection 读取标题而不是数据以避免读取整个响应。

于 2012-03-27T23:23:30.717 回答