为什么Tomcat会将HEAD和GET请求的不同标头返回给我的RESTful API? [英] Why Tomcat returns different headers for HEAD and GET requests to my RESTful API?

查看:132
本文介绍了为什么Tomcat会将HEAD和GET请求的不同标头返回给我的RESTful API?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最初的目的是验证 HTTP分块传输。但是偶然发现了这种不一致。



API旨在将文件返回给客户端。我使用 HEAD GET 方法。 返回不同的标头。



对于 GET ,我得到以下标题:(这是我的预期。)





根据这个帖子 HEAD GET 应该返回相同的标题,但不一定



我的问题是:



如果 Transfer-Encoding:chunked 用于,因为 fil e被动态地提供给客户端,Tomcat服务器无法事先知道它的大小 ,当 Content-Length > HEAD 使用方法? Tomcat只是干运行处理程序并计算所有文件字节?为什么不简单地返回相同的 Transfer-Encoding:chunked 标题?



以下是我实现的RESTful API使用 Spring Web MVC

  @RestController 
公共类ChunkedTransferAPI {

@Autowired
ServletContext servletContext;

@RequestMapping(value =bootfile.efi,method = {RequestMethod.GET,RequestMethod.HEAD})
public void doHttpBoot(HttpServletResponse response){

String filename =/ bootfile.efi;
try {
ServletOutputStream output = response.getOutputStream();
InputStream input = servletContext.getResourceAsStream(filename);
BufferedInputStream bufferedInput = new BufferedInputStream(input);
int datum = bufferedInput.read();
while(datum!= -1){
output.write(datum);
datum = bufferedInput.read();
}
output.flush();
output.close();

} catch(IOException e){
// TODO自动生成的catch块
e.printStackTrace();
}

}

}

ADD 1



在我的代码中,我没有显式添加任何标题,那么它必须是添加<$ c的Tomcat $ c> Content-Length 和 Transfer-Encoding 标头,因为它认为合适。



那么,Tomcat决定发送哪些标题的规则是什么?



ADD 2



可能与Tomcat的工作方式有关。我希望有人能在这里阐明一些。否则,我将调试到Tomcat 8的源代码并共享结果。但这可能需要一段时间。



相关:




解决方案

虽然看起来很奇怪,但它可能会有意义的是只发送大小以响应 HEAD 请求并按照 GET 请求进行分块,具体取决于服务器必须返回的数据类型。



虽然你的API似乎提供了一个静态文件,但你也谈到动态创建的文件或数据,所以我我将在这里进行一般性讨论(也适用于一般的网络服务器)。



首先让我们看一下 GET HEAD




  • 使用 GET 客户端正在请求整个文件或数据(或一系列数据),并希望尽快。因此,没有特定的理由让服务器首先发送数据大小,特别是当它可以在分块模式下更快/更快地开始发送时。因此,首选最快的方式(无论如何客户端将具有下载后的大小)。


  • 使用 HEAD 另一方面,客户通常需要一些特定的信息。这可能只是检查存在或最后更改,但如果客户端需要某个部分数据(包括范围请求,包括检查是否支持该请求的范围请求),也可以使用它),或者只是出于某种原因需要事先知道数据的大小。




不要看一些可能的场景:



静态文件



HEAD :没有理由不在响应标头中包含大小,因为该信息可用。



GET :大多数时候大小将包含在标题中并且数据一次性发送,除非有特定的性能原因以块的形式发送它。另一方面,你似乎期待为你的文件进行分块传输,所以这在这里有意义。



实时日志文件



好的,有点奇怪,但有可能:下载文件,其大小可能会在下载时发生变化。



HEAD :再次,客户端可能想要大小,服务器可以在标题中的特定时间轻松提供文件的大小。



GET :由于可以在下载时添加日志,因此预先知道大小。只有选项是发送分块。



具有固定大小记录的表



让我们假设服务器需要发回一个来自多个来源/数据库的固定长度记录的表:



HEAD :客户可能需要大小。服务器可以快速查询每个数据库中的 count ,并将计算出的大小发送回客户端。



GET :首先,不是在每个数据库中对 count 进行查询,服务器最好开始以块的形式从每个数据库发送结果记录。



动态生成的zip文件



可能不常见,但这是一个有趣的例子。



想象一下,你想根据一些参数向用户提供动态生成的zip文件。



让我们先来看看zip文件的结构:



有两个部分:首先是每个文件都有一个块:一个小标题,后跟该文件的压缩数据。然后是zip文件中所有文件的列表(包括大小/位置)。



因此,可以在磁盘上预先生成每个文件的准备好的块(以及存储在某些数据结构中的名称/大小。



HEAD :客户可能想知道这里的大小。服务器可以很容易地计算出所有需要块的大小+第二部分的大小和里面的文件列表。



如果客户想要提取一个单个文件,它可以直接请求文件的最后一部分(带有范围请求)来获取列表,然后用第二个请求请求该单个文件。虽然大小不一定需要得到最后一个n如果你想将不同的部分存储在与完整zip文件相同大小的稀疏文件中,那么它可能很方便。



GET :不需要先进行计算(包括生成第二部分以了解其大小)。它会b更好更快地开始以块的形式发送每个块。



完全动态生成的文件



在这种情况下,将大小返回到 HEAD 请求当然不是非常有效,因为整个文件需要生成到知道它的大小。


My initial purpose was to verify the HTTP chunked transfer. But accidentally found this inconsistency.

The API is designed to return a file to client. I use HEAD and GET methods against it. Different headers are returned.

For GET, I get these headers: (This is what I expected.)

For HEAD, I get these headers:

According to this thread, HEAD and GET SHOULD return identical headers but not necessarily.

My question is:

If Transfer-Encoding: chunked is used because the file is dynamically fed to the client and Tomcat server cannot know its size beforehand, how could Tomcat know the Content-Length when HEAD method is used? Does Tomcat just dry-run the handler and count all the file bytes? Why doesn't it simply return the same Transfer-Encoding: chunked header?

Below is my RESTful API implemented with Spring Web MVC:

@RestController
public class ChunkedTransferAPI {

    @Autowired
    ServletContext servletContext;

    @RequestMapping(value = "bootfile.efi", method = { RequestMethod.GET, RequestMethod.HEAD })
    public void doHttpBoot(HttpServletResponse response) {

        String filename = "/bootfile.efi";
        try {
            ServletOutputStream output = response.getOutputStream();
            InputStream input = servletContext.getResourceAsStream(filename);
            BufferedInputStream bufferedInput = new BufferedInputStream(input);
            int datum = bufferedInput.read();
            while (datum != -1) {
                output.write(datum);
                datum = bufferedInput.read();
            }
            output.flush();
            output.close();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

ADD 1

In my code, I didn't explicitly add any headers, then it must be Tomcat that add the Content-Length and Transfer-Encoding headers as it sees fit.

So, what are the rules for Tomcat to decide which headers to send?

ADD 2

Maybe it's related to how Tomcat works. I hope someone can shed some light here. Otherwise, I will debug into the source of Tomcat 8 and share the result. But that may take a while.

Related:

解决方案

Although it seems strange, it might make sense to send the size only in response to a HEAD request and chunked in response to a GET request, depending on the type of data that has to be returned by the server.

While your API seems to provide a static file, you also talk about dynamically created files or data, so I will be talking in general here (also for webservers in general).

First let's have a look at the different usages for GET and HEAD:

  • With GET the client is requesting the whole file or data (or a range of the data), and wants it as fast as possible. So there is no specific reason for the server to send the size of the data first, especially when it could start sending faster/sooner in chunked mode. So the fastest possible way is preferred here (the client will have the size after the download anyway).

  • With HEAD on the other hand, the client usually wants some specific information. This could just be a check on existance or 'last-changed', but it could also be used if the client wants a certain part of the data (with a range request, including a check to see if range requests are supported for that request), or just needs to know the size of the data up front for some reason.

Lest's look at some possible scenarios:

Static file:

HEAD: there's no reason to not include the size in the response-header because that information is available.

GET: most of the time the size will be inluded in the header and the data sent in one go, unless there are specific performance reasons to send it in chunks. On the other hand it seems you are expecting chunked transfer for you file, so this could make sense here.

Live logfile:

Ok, somewhat strange, but possible: downloading a file where the size could change while downloading.

HEAD: again, the client probably wants the size, and the server can easily provide the size of the file at that specific time in the header.

GET: since loglines could be added while downloading, the size is unknown up front. Only option is to send chunked.

Table with fixed-sized records:

Let's imagine a server needs to send back a table with fixed-length records coming from multiple sources/databases:

HEAD: size is probably wanted by the client. The server could quickly do a query for count in each database, and send the calculated size back to the client.

GET: instead of doing a query for count in each database first, the server better starts sending the resulting records from each database in chunks.

Dynamically generated zip-files:

Maybe not common, but an interesting example.

Imagine you want to provide dynamically generated zip-files to the user based on some parameters.

Let's first have a look at the structure of a zip-file:

There are two parts: first there's a block for each file: a small header followed by the compressed data for that file. Then there's a list of all the files inside the zip-file (including sizes/positions).

So the prepared blocks for each file could be pre-generated on disk (and the names/sizes stored in some data structure.

HEAD: the client probably wants to know the size here. The server can easily calculate the size of all the needed blocks + the size of the second part with the list of the files inside.

If the client wants to extract a single file, it could directly ask for the last part of the file (with a range-request) to have the list, and then with a second request ask for that single file. Although the size is not necessarily needed to get the last n bytes, it could be handy if for example if you wanted to store the different parts in a sparse file with the same size of the full zip-file.

GET: no need to do the calculations first (including generating the second part to know its size). It would be better and faster to just start sending each block in chunks.

Fully dynamically generated file:

In this case it wouldn't be very efficient to return the size to a HEAD request of course, since the whole file would need to be generated just to know its size.

这篇关于为什么Tomcat会将HEAD和GET请求的不同标头返回给我的RESTful API?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆