英文:
Will HTTP ResponseWriter's write function buffer in Go?
问题
假设我们有一个处理HTTP请求的函数,类似于:
func handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("first piece of data"))
// do something
w.Write([]byte("second piece of data"))
}
我想知道第一次调用w.Write()
是否已经刷新到客户端了?
如果已经刷新了,那么我们实际上向客户端发送了两次响应,这很奇怪,因为在第二次调用写入之前,我们如何确定Content-Length
?
如果没有刷新(假设数据在本地缓冲),那么如果我们在第一次调用时写入了大量数据会怎么样?(会不会导致堆栈溢出?)
对于任何解释,我都会非常感激!
英文:
Assume that we have a function handling an HTTP Request, something like:
func handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("first piece of data"))
// do something
w.Write([]byte("second piece of data"))
}
I'm wondering that if the first call to w.Write() is flushed to client or not?
If it is flushed, then we actually responses to clients twice, this is strange because how can we determine Content-Length
before the second call to write?
If it is not flushed (say the data is buffered locally), then what if we write a huge amount of data at the first call? (will that stack overflow?)
Any explanation will be appreciated!
答案1
得分: 6
我想知道第一次调用w.Write()是否会立即刷新到客户端?
net/http
的默认ResponseWriter
在写入到net.Conn
时有一个(目前为4KB)的输出缓冲区。此外,操作系统通常会对套接字的写入进行缓冲。因此,在大多数情况下会进行某种形式的缓冲。
如果刷新了,那么我们实际上会向客户端发送两次响应,这很奇怪,因为在第二次调用写入之前,我们如何确定Content-Length?
好吧,HTTP 1.1允许持久连接。这种响应通常不包括Content-Length
头。此外,还有HTTP trailers。
如果您的客户端不支持HTTP 1.1和持久连接,它们将具有某种读取超时,在此期间您可以随意多次写入连接;这是一个响应。
这更多与TCP套接字和HTTP实现的性质有关,而不是与Go有关。
如果没有刷新(假设数据在本地缓冲),那么如果我们在第一次调用时写入了大量数据会怎么样?(会导致堆栈溢出吗?)
不会,将缓冲区分配到堆栈上是没有意义的——缓冲区的内容将存储在堆上。如果达到了进程的内存限制,应用程序将会发生“内存不足”的恐慌。
另请参阅:
编辑以回答您在评论中的问题:
分块传输编码是HTTP 1.1规范的一部分,不支持HTTP 1.0。
编辑以澄清:
只要您写入响应的两部分所需的总时间不超过客户端的读取超时时间,并且您没有指定Content-Length
头,您只需写入响应然后关闭连接。这是完全可以的,不是“hacky”的。
英文:
> I'm wondering that if the first call to w.Write() is flushed to client or not?
net/http
's default ResonseWriter
has a (currently 4KB) large output buffer over the net.Conn
it writes to. Additionally, the OS normally will buffer writes to a socket. So in most cases some kind of buffering takes place.
> If it is flushed, then we actually responses to clients twice, this is strange because how can we determine Content-Length before the second call to write?
Well there's HTTP 1.1 which allows persistent connections. Such responses usually don't include a Content-Length
header. Additionally, there's HTTP trailers.
If your client does not support HTTP 1.1 and persistent connections they will have some sort of read timeout, during this time you can write to the connection as many times as you like; it's one response.
This has more to do with the nature of TCP sockets and HTTP implementations than Go.
> If it is not flushed (say the data is buffered locally), then what if we write a huge amount of data at the first call? (will that stack overflow?)
No, allocating a buffer on the stack makes no sense – the buffer's body will live on the heap. If you hit your per-process memory limit your application will panic "out of memory".
See also:
- https://stackoverflow.com/questions/9075455/how-to-turn-off-buffering-on-write-system-call
*TCPConn.SetNoDelay
Edit to answer your question in the comments:
Chunked Transfer Encoding is part of the HTTP 1.1 specification and not supported in HTTP 1.0.
Edit to clarify:
As long as the total time it takes you to write both parts of your response does not exceed your client's read time out, and you don't specify a Content-Length
header you just write your response and then close the connection. That's totally OK and not "hacky".
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论