2014-09-29 2 views
4

Предположим, что сервер должен отвечать на некоторые данные клиенту, а данные поступают из файла на локальном диске. Тогда мы пишем,При ответе на использование io.Copy, кто должен нести ответственность за ошибку?

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

То, что я имею в виду есть io.Copy() первый пишет копирует данные заголовка, а затем из f в w.

Когда err не nil (скажем unexpected EOF), клиент по-прежнему получает код состояния 200, хотя тело ответа содержит что-то неправильно.

Возможно, локальный диск поврежден, или, возможно, сеть клиента повреждена. Как определить
, вызван ли сервер err сервером или клиентом?

+0

Вы также должны рассмотреть возможность использования 'http.ServeFile' для возврата содержимого. Если вы не можете по какой-то причине или если это волшебство делает больше, чем вы хотите/нуждаетесь, возьмите гангстер о том, как это реализовано. Вы можете увидеть, как он справляется с ошибками и копирует эту обработку в ваш код снова, предполагая, что вы не можете просто использовать 'ServeFile' самостоятельно. –

ответ

6

io.Copy звонки Write на цель io.Writer. http.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) 

Так что да, если ваш HD был потерпеть неудачу когда-нибудь во время Write операции вы» d уже написали ответ 200 OK, однако, если ваш ответ указывает , клиент будет знать, что что-то не так, когда длина ответа не совпадает.

В случае HTTP 1.1 и кодированной передачи кодирования теоретически вы можете указать заголовок ошибки после ответа в трейлере HTTP. К сожалению, HTTP-трейлеры не поддерживаются ни одним из самых популярных веб-браузеров.

Вклад @OneOfOne: io.Copy 's error не укажет, какой конец не удался; если сервер или клиент.

В результате мы не можем указать, что ошибка должна регистрироваться как 4xx или 5xx, правильно?

Если вы регистрируете заголовок состояния HTTP, зарегистрируйте то, что вы отправили клиенту в качестве ответа; не то, что должно было быть.

+1

err Мне больше нравится ваш ответ, не стесняйтесь упомянуть, что 'io.Copy' не укажет, какой конец не удалось. Я удаляю свой ответ. – OneOfOne

+0

Итак, в результате мы не можем указать, что ошибка должна регистрироваться как 4xx или 5xx, правильно? – dastan

+0

@ user3505816 Я отредактировал ответ. – thwd

4

При копировании непосредственно из файла в автоответчик единственный способ сообщить клиенту, что что-то не так, - отправить тело неполного ответа.

Чтобы заставить сервер отправить неполное тело ответа, указать длину содержимого перед копированием тела:

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

обработчик должен просто вернуться после копирования тела, ошибки или нет.

Сервер проверяет, указал ли обработчик количество байтов, указанное в заголовке длины содержимого. Если обработчик не записывал это количество байтов, сервер закрывает соединение.

Клиент может обнаружить, что соединение было закрыто до того, как все тело было прочитано.Многие HTTP-клиентские библиотеки сообщают об ошибке в этом сценарии.

Если вы забудете файл в памяти перед началом записи ответа, вы можете установить код состояния ответа, чтобы указать на ошибку. Если файл большой, вы можете не захотеть буферизировать.

Трудно, чтобы обработчик обнаружил, что io.Copy не удалось из-за ошибки чтения файла или записи ошибки клиенту. Учитывая количество возможных путей кода (разные ОС, TLS или нет, дополнительные оптимизации в io.Copy, ...), существует много потенциальных ошибок, возвращаемых из io.Copy. Ошибки могут быть даже не уникальными между ошибками файлов и клиентов.

Задание длины содержимого перед копированием файла имеет дополнительные преимущества: сервер всегда использует наиболее эффективную кодировку передачи (идентификационную кодировку), когда длина содержимого известна. В некоторых операционных системах операция io.Copy будет выполняться ядром.

+0

Если я использую io.Copy для ответа файла клиенту, я также использую 'chunked-encoding'. И если я не укажу «Content-Length», клиент не сможет обнаружить неполноту тела ответа. Согласно вашему объяснению, полезно указывать 'Content-Length' даже когда' chunked-encoding', но будут ли эти два поля заголовка конфликтуют? – dastan

Смежные вопросы