URLConnection无法通过代理正确处理内容长度

问题描述:

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

首先我检查了代理真的返回Content-Lengthlynxwget通过代理也在努力;没有别的路可走互联网从本地网络):我写

$ 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中的错误,所以我已经打开了新的bug#7168608

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

为什么你需要的内容长度?数据的实际流是否正确?如果是这样,你不需要内容的长度,一切工作正常。 – jtahlborn 2012-04-03 14:08:37

+0

@jtahlborn:实际的URL是正确的(它是公共FTP,所以你可以测试)。我需要了解内容长度**,而不需要读取流到最后,显然可以做到这一点。 – 2012-04-03 17:43:40

一两件事来检查我会做的是实际读取响应(注销我的头顶,所以我们期待的错误):

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

如果你所得到的大小为好,再看看为了使URLConnection读取标题而不是数据以避免读取整个响应。

+0

倒霉:这不起作用(请参阅我的更新回答)。如果您使用任何公开代理,您可以测试自己。 – 2012-03-28 20:30:16

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

你触犯属于HTTP 1.1规范的以下两个部分组成:

4.4 Message Length

  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标头。

这样的URLConnection然后忽略Content-Length头,按照该规范,因为它的存在毫无意义的分块转让

在调试器的截图,目前还不清楚该Transfer-Encoding是否存在头部。请让我们知道...

在进一步调查 - 似乎山猫不会显示所有返回时,你发出一个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标头。

+0

为什么在分块转移的情况下它没有意义?如果服务器可以传递整个流的长度,为什么消费者不能使用这些信息?这是“URLConnection”的一项任务,用于收集所有块并隐藏消费者的协议详细信息。但是,好的,如果规格如此说...在我从'lynx'输出的问题中,你可以看到'Transfer-Encoding'不存在,所以我不能为你的答案投票。 – 2012-04-03 11:18:42

+0

在你的问题中的l command命令看起来很奇怪。首先'-head'不适用于http URL - 所示的命令对我来说不适用于Lynx 2.8.7rel.2。其次,如果响应是通过代理服务器发出的,那么您会希望在响应中看到Via:标头。 (尽管代理人并不总是尊重这一点) – sw1nn 2012-04-03 11:37:15

+0

感谢您的评论。我给出的Lynn命令对我来说工作得很好(测试版本为v2.8.6rel.5)。 HTTP HEAD适用于任何URL,如果你通过代理**去**:你可以尝试任何开放代理。如果“Via”不存在,这并不意味着答复不是来自代理。如果你希望我可以把'wget'命令输出,但它会得到相同的输出结果。正如我所提到的,我不能以另一种方式上网:由于防火墙原因,这是不可能的。事实上,我们的代理配置为不将'Via'返回给客户端。 'lynx'的输出与Java中的输出完全相同,请以实际情况为准。 – 2012-04-03 13:04:30

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

+0

@dma_k wrt在jdk中的错误 - 显然FTP客户端代码已经完全彻底检查JDK 7. http://bugs.sun.com/view_bug.do?bug_id=6893702和http://bugs.sun.com /view_bug.do?bug_id=6519647似乎相关(虽然不完全是你的问题)。你有没有试过JDK 7? – sw1nn 2012-04-03 21:53:08

+0

2all:如果您确认问题并且可以将其报告给Sun +分享该链接,我会奖励赏金(8小时后)。即使问题在JDK7中解决(我没有检查过),我也无法从中受益:生产AS在Java6中,并且将在未来几年内实现。更糟的是:代码应该符合1.5。 – 2012-04-04 15:12:34

+1

@dma_k - 为什么是其他人的工作来确认您的问题?我给你我认为是正确的答案。应该很简单,以确认它。 – jtahlborn 2012-04-04 18:11:48