HttpURLConnection getInputStream() 有时包含响应标头

发布于 2024-11-12 23:38:57 字数 3101 浏览 4 评论 0原文

我的 Android 应用程序使用 HttpURLConnection 对象从我们的服务器请求信息。服务器返回一些文本——可能是一点(~50 个字符)或很多(12 MB),具体取决于请求。 MIME 类型是文本/纯文本。

在我最初的实现中,我很失望地发现 getInputStream() 从服务器返回整个 HTTP 响应(包括标头),尽管文档说它只包含响应正文。哦,好吧...我实现了一个小型状态机来读取和存储标头,并在代码中提供接口以允许调用者访问标头字段。从 AsyncTask 调用它以使其脱离 UI 线程并获得简单的进度跟踪方法。一切都很好。

我最近添加了另一个从服务器请求信息的功能,因此我使用了相同的“http 连接包装器”对象,该对象期望在主体流中看到标头。这次,使用 Thread 对象将下载推离 UI 线程很方便。 (我不需要进度跟踪,讽刺的是,Thread 比“方便对象”AsyncTask 更容易实现。)有趣的是,当从 Thread 对象调用时,我没有看到 getInputStream() 流中的标头。

唯一的区别是,在一种情况下,我从 AsyncTask 派生的对象内部发出请求,而在另一种情况下,我从 Thread 派生的对象内部发出请求。

当我从 AsyncTask 使用 HttpUrlConnection 时,我在输入流中看到标头。当我从线程使用 HttpUrlConnection 时,我只看到响应正文。根据文档,后者是“正确”的行为。

我尝试过切换方法。也就是说,我在线程而不是 AsyncTask 中发出第一个请求。如果我这样做,我就看不到标题。当我在 AsyncTask 而不是线程中发出第二个请求时,我在输入流中看到标头,其中只需要正文。所以这个问题似乎与我是在 AsyncTask 还是在 Thread 中有关。

显然,我可以选择一种或另一种方法来使我的 HTTP 活动脱离 UI 线程,但我想了解发生了什么,以便无论如何调用它,我都可以使其行为相同。我喜欢使用 AsyncTask,因为它有一个简单的内置进度跟踪机制,但这种方法无法正常工作。

有想法吗?建议?

更多信息

我已将 HttpURLConnection 访问封装在一个如下所示的小函数中(实际代码有一个委托,它将接收到的字符串发送到该委托,但我已将其简化以进行测试)

/**
 * Reads the data at the URL.
 * @param urlString The URL to read
 */
public void Get(
    String urlString)
    {
    URL url;
    BufferedInputStream in;
    try
        {
        url = new URL(urlString);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept", "text/*");
        in = new BufferedInputStream(conn.getInputStream(), 8192);

        byte [] buffer = new byte[8192];
        StringBuilder stringBuffer = new StringBuilder(16384);

        int read;
        while ((read = in.read(buffer)) != -1)
            {
            String tempstr = new String(buffer, 0, read, "ISO-8859-1");
            System.out.println(tempstr);
            }
        return true;
        }
    catch (IOException e)
        {
        return false;
        }

:从两个地方调用这个函数。为了进行测试,我有两个地方都从我们的服务器请求相同的页面。第一个是在扩展 AsyncTask 的对象中。在 doInBackground() 中,我实例化包含 Get() 的对象,然后调用 Get()。第二个是在扩展 Thread 的对象中,我在其中实例化包含 Get() 的对象,然后在 run() 中调用 Get()。在这两种情况下我都请求相同的页面。

结果是,当我从 AsyncTask 调用时,我看到所有响应标头。我实际上看到的是原始 HTTP——包括传输编码“分块”时的块长度。当我从我的线程调用时,我只得到 HTML 正文,这正是我所期望的。

为了进行测试,我在服务器上编写了一个返回原始请求的脚本。这是我得到的结果(注意“awebsite”不是我的服务器的名称):

在线程中运行时:

Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0

在 AsyncTask 中运行时:

[CRLF]
HTTP/1.1 200 OK
Date: Tue, 31 May 2011 23:29:20 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Type: text/html
Set-Cookie: ASPSESSIONIDCQSTTQAQ=OHBIFGJCPEAAHGNHICPKOKFO; path=/
Cache-control: private
Transfer-Encoding: chunked
[CRLF]
53
Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0
[CRLF]
0
[CRLF]

请注意,这两种情况似乎都发送相同的请求。然而,在我从 AsyncTask 调用 Get() 的情况下,有些东西会欺骗 HttpURLConnection 为我提供响应标头和正文。

更令人困惑的是,访问 www.google.com 可以按预期工作——我从未看到标题与正文混合在一起。因此,这似乎表明服务器端出现问题(这让我想知道服务器如何知道失败),或者服务器响应方式的某些问题使 HttpURLConnection 在 AsyncTask 中运行时感到困惑。

My Android app requests information from our server using an HttpURLConnection object. The server returns some text -- could be a little (~50 characters) or a lot (12 MB) depending on the request. The mime type is text/plain.

Upon my initial implementation, I was disappointed to discover that getInputStream() was returning the entire HTTP response from the server -- including the headers -- even though the documentation said it contains only the response body. Oh well... I implemented a little state machine to read and store the headers and provided interfaces in my code to allow callers to access header fields. Called it from an AsyncTask to get it off the UI thread and to get an easy progress tracking method. All was well.

I've recently added another feature that requests information from the server, so I used my same "http connection wrapper" object, which expects to see headers in the body stream. This time, it was convenient to use a Thread object to push the download off the UI thread. (I didn't need the progress tracking and ironically, Thread is easier to implement than the "convenience object" AsyncTask.) Interestingly, I was not seeing the headers in the getInputStream() stream when called from a Thread object.

The only difference is that in one case I'm making the request from inside an object derived from AsyncTask and in the other case I'm making the request from inside an object derived from Thread.

When I use HttpUrlConnection from an AsyncTask, I see headers in the input stream. When I use HttpUrlConnection from a Thread, I see only the response body. The latter is the "correct" behavior according to the documentation.

I've tried switching methods. That is, I make my first request in a Thread instead of an AsyncTask. If I do that, I do not see the headers. When I make my second request in an AsyncTask instead of a Thread, I see the headers in the input stream where only the body is expected. So the issue seems to be related to whether I'm in an AsyncTask or in a Thread.

Obviously I could choose one or the other methods of getting my HTTP activity off the UI thread, but I'd like to understand what's going on so I can make it behave identically regardless of how it's called. And I like using AsyncTask because it has an easy built-in progress tracking mechanism, but that's the method that isn't working right.

Ideas? Suggestions?

MORE INFORMATION

I've encapsulated my HttpURLConnection access in a little function that looks like this (the actual code has a delegate to which it sends the received string, but I've simplified it for testing):

/**
 * Reads the data at the URL.
 * @param urlString The URL to read
 */
public void Get(
    String urlString)
    {
    URL url;
    BufferedInputStream in;
    try
        {
        url = new URL(urlString);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept", "text/*");
        in = new BufferedInputStream(conn.getInputStream(), 8192);

        byte [] buffer = new byte[8192];
        StringBuilder stringBuffer = new StringBuilder(16384);

        int read;
        while ((read = in.read(buffer)) != -1)
            {
            String tempstr = new String(buffer, 0, read, "ISO-8859-1");
            System.out.println(tempstr);
            }
        return true;
        }
    catch (IOException e)
        {
        return false;
        }

I call this function from two places. For testing, I have both places requesting the same page from our server. First is in an object that extends AsyncTask. In doInBackground(), I instantiate the object that contains Get(), then call Get(). Second is in an object that extends Thread, where I instantiate the object that contains Get() then call Get() in run(). In both cases I request the same page.

The result is when I call from my AsyncTask, I see ALL the response headers. What I actually see is the raw HTTP -- including chunk lengths when Transfer-Encoding is "chunked". When I call from my Thread, I get just the body of the HTML, which is what I expect.

For testing, I wrote a script on the server that returns the raw request. Here's what I get (note "awebsite" is not the name of my server):

When running in a Thread:

Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0

When running in an AsyncTask:

[CRLF]
HTTP/1.1 200 OK
Date: Tue, 31 May 2011 23:29:20 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Type: text/html
Set-Cookie: ASPSESSIONIDCQSTTQAQ=OHBIFGJCPEAAHGNHICPKOKFO; path=/
Cache-control: private
Transfer-Encoding: chunked
[CRLF]
53
Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0
[CRLF]
0
[CRLF]

Note that both situations seem to send the same request. However, something is tricking HttpURLConnection into giving me the response headers right along with the body in the case where I call Get() from an AsyncTask.

To further confuse things, accessing www.google.com works as expected -- I never see headers mixed with the body. So that would seem to indicate there's either something wrong on the server side (which makes me wonder how the server knows to fail) or there's something about how the server responds that confuses HttpURLConnection when it's running in an AsyncTask.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

勿挽旧人 2024-11-19 23:38:57

我预计您的服务器端生成中存在错误:也许有一个响应标头有几个嵌入的换行符,并且 http 解析器将其视为标头的末尾。

I expect there is an error in your server side generation: perhaps there is a response header that has a couple of embedded newlines and the http parser is taking that to be the end of the headers.

哎呦我呸! 2024-11-19 23:38:57
  1. 检查 conn.getResponseCode()。如果它是-1,你不应该尝试读取流。
  2. 如果您使用的是 2.1 或更低版本,请执行以下操作:System.setProperty("http.keepAlive", "false");
    更多详细信息请点击这里HttpURLConnection.getResponseCode() 在第二次调用时返回 -1
  1. Check conn.getResponseCode(). If it's -1 you should not try to read the stream.
  2. If you're on 2.1 or lower do the following: System.setProperty("http.keepAlive", "false");
    More details here HttpURLConnection.getResponseCode() returns -1 on second invocation.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文