2014-09-29 57 views
4

假设服务器需要向客户端回复一些数据,并且数据来自本地磁盘上的文件。然后我们写,当使用io.Copy进行响应时,谁应该对错误负责?

n, err := io.Copy(w, f) // w is the ResponseWriter and f is the *os.File 

我在想什么就是io.Copy()头,然后将数据写入第一从fw

err不是nil(比如unexpected EOF)时,尽管响应主体包含错误,客户端仍然获得状态码200。

也许本地磁盘坏了,或者客户端的网络坏掉了。我们如何确定
err是由服务器还是客户端造成的?

+0

您还应该考虑使用'http.ServeFile'来返回内容。如果你出于某种原因不能,或者如果它奇迹般地超过了你的需要,那么就如何实施它。您可以看到它如何处理错误问题并将该处理复制到您的代码中 - 假设您不能自己使用“ServeFile”。 – 2014-09-30 02:07:14

回答

6

io.Copy对目标io.Writer调用Writehttp.ResponseWriter的对Write方法文档指定此行为:

// Write writes the data to the connection as part of an HTTP reply. 
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) 
// before writing the data. If the Header does not contain a 
// Content-Type line, Write adds a Content-Type set to the result of passing 
// the initial 512 bytes of written data to DetectContentType. 
Write([]byte) (int, error) 

这意味着它会先调用WriteHeader

// WriteHeader sends an HTTP response header with status code. 
// If WriteHeader is not called explicitly, the first call to Write 
// will trigger an implicit WriteHeader(http.StatusOK). 
// Thus explicit calls to WriteHeader are mainly used to 
// send error codes. 
WriteHeader(int) 

所以,是的,如果你的硬盘是Write操作过程中失败的somewhen你“ d已经写了一个200 OK响应,但是,如果您的响应指定了Content-Length,那么当您的响应长度不匹配时,客户端就会知道有问题。

对于HTTP 1.1和分块传输编码,理论上可以在HTTP预告片中的响应之后指定失败标头。遗憾的是,当前最常用的Web浏览器不支持HTTP预告片。

@OneOfOne的贡献:io.Copy的错误不会指定哪一端失败;如果服务器或客户端。

因此,我们不能指出err应该被记录为4xx或5xx,对吧?

如果您正在记录HTTP状态标题,然后记录您发送客户端作为响应;不是它应该的。

+1

err我更喜欢你的答案,随意提及'io.Copy'不会指定哪一端失败。我正在删除我的答案。 – OneOfOne 2014-09-29 10:15:13

+0

因此,我们不能指出err应该记录为4xx或5xx,对吧? – dastan 2014-09-29 10:18:16

+0

@ user3505816我编辑了答案。 – thwd 2014-09-29 10:23:45

4

当直接从文件复制到响应写入程序时,告诉客户端有问题的唯一方法是发送不完整的响应主体。

要强制服务器发送一个不完整的响应体,复制体之前指定的内容长度:

w.Header().Set("Content-Length", strconv.Itoa(fileLen)) 

处理程序应该复制体,错误或不经过简单的返回。

服务器检查处理程序是否写入了内容长度标题中指定的字节数。如果处理程序没有写入该字节数,则服务器关闭连接。

客户端可以在完整主体读取之前检测到连接已关闭。在这种情况下,许多HTTP客户端库将报告错误。

如果在开始编写响应之前将文件缓冲在内存中,则可以设置响应状态码来指示错误。如果文件很大,您可能不想缓冲。

处理程序难以检测io.Copy是由于读取文件时发生错误还是写入客户端时发生错误而失败。鉴于所涉及的可能代码路径的数量(不同的操作系统,TLS或不可用,io.Copy中的可选优化,...),io.Copy会返回很多潜在的错误。文件和客户端错误之间的错误甚至可能不是唯一的。

复制该文件之前指定的内容长度具有额外的好处:服务器总是使用最有效的传输编码(身份编码)当内容长度是已知的。在某些操作系统上,io.Copy操作将由内核完成。

+0

如果我使用io.Copy将文件回复给客户端,那么我也使用'chunked-encoding'。如果我不指定'内容Length'则客户端无法检测响应主体的不完备性。根据你的解释,这是很好的指定'内容Length'即使'分块,encoding',而是将两个头字段的冲突? – dastan 2014-09-30 03:11:29