2014-09-29 47 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