Final fix for writeheader
This commit is contained in:
@@ -124,6 +124,7 @@ func (f *File) servePreloadedContent(w http.ResponseWriter, r *http.Request) err
|
|||||||
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, size))
|
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, size))
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", end-start+1))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", end-start+1))
|
||||||
w.Header().Set("Accept-Ranges", "bytes")
|
w.Header().Set("Accept-Ranges", "bytes")
|
||||||
|
w.WriteHeader(http.StatusPartialContent)
|
||||||
|
|
||||||
_, err = w.Write(content[start : end+1])
|
_, err = w.Write(content[start : end+1])
|
||||||
return err
|
return err
|
||||||
@@ -132,6 +133,7 @@ func (f *File) servePreloadedContent(w http.ResponseWriter, r *http.Request) err
|
|||||||
// Full content
|
// Full content
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
||||||
w.Header().Set("Accept-Ranges", "bytes")
|
w.Header().Set("Accept-Ranges", "bytes")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
_, err := w.Write(content)
|
_, err := w.Write(content)
|
||||||
return err
|
return err
|
||||||
@@ -195,9 +197,13 @@ func (f *File) streamWithRetry(w http.ResponseWriter, r *http.Request, retryCoun
|
|||||||
return retryErr
|
return retryErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := f.streamBuffer(w, resp.Body); err != nil {
|
// Determine status code based on range request
|
||||||
return err
|
statusCode := http.StatusOK
|
||||||
|
if isRangeRequest == 1 {
|
||||||
|
statusCode = http.StatusPartialContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set headers before streaming
|
||||||
if contentLength := resp.Header.Get("Content-Length"); contentLength != "" {
|
if contentLength := resp.Header.Get("Content-Length"); contentLength != "" {
|
||||||
w.Header().Set("Content-Length", contentLength)
|
w.Header().Set("Content-Length", contentLength)
|
||||||
}
|
}
|
||||||
@@ -205,10 +211,14 @@ func (f *File) streamWithRetry(w http.ResponseWriter, r *http.Request, retryCoun
|
|||||||
if contentRange := resp.Header.Get("Content-Range"); contentRange != "" && isRangeRequest == 1 {
|
if contentRange := resp.Header.Get("Content-Range"); contentRange != "" && isRangeRequest == 1 {
|
||||||
w.Header().Set("Content-Range", contentRange)
|
w.Header().Set("Content-Range", contentRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := f.streamBuffer(w, resp.Body, statusCode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) streamBuffer(w http.ResponseWriter, src io.Reader) error {
|
func (f *File) streamBuffer(w http.ResponseWriter, src io.Reader, statusCode int) error {
|
||||||
flusher, ok := w.(http.Flusher)
|
flusher, ok := w.(http.Flusher)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("response does not support flushing")
|
return fmt.Errorf("response does not support flushing")
|
||||||
@@ -216,12 +226,19 @@ func (f *File) streamBuffer(w http.ResponseWriter, src io.Reader) error {
|
|||||||
|
|
||||||
smallBuf := make([]byte, 64*1024) // 64 KB
|
smallBuf := make([]byte, 64*1024) // 64 KB
|
||||||
if n, err := src.Read(smallBuf); n > 0 {
|
if n, err := src.Read(smallBuf); n > 0 {
|
||||||
|
// Write status code just before first successful write
|
||||||
|
w.WriteHeader(statusCode)
|
||||||
|
|
||||||
if _, werr := w.Write(smallBuf[:n]); werr != nil {
|
if _, werr := w.Write(smallBuf[:n]); werr != nil {
|
||||||
return werr
|
if isClientDisconnection(werr) {
|
||||||
|
return &streamError{Err: werr, StatusCode: 0, IsClientDisconnection: true}
|
||||||
|
}
|
||||||
|
// Headers already sent, can't send HTTP error response
|
||||||
|
return &streamError{Err: werr, StatusCode: 0, IsClientDisconnection: false}
|
||||||
}
|
}
|
||||||
flusher.Flush()
|
flusher.Flush()
|
||||||
} else if err != nil && err != io.EOF {
|
} else if err != nil && err != io.EOF {
|
||||||
return err
|
return &streamError{Err: err, StatusCode: http.StatusInternalServerError}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 256*1024) // 256 KB
|
buf := make([]byte, 256*1024) // 256 KB
|
||||||
@@ -232,7 +249,8 @@ func (f *File) streamBuffer(w http.ResponseWriter, src io.Reader) error {
|
|||||||
if isClientDisconnection(writeErr) {
|
if isClientDisconnection(writeErr) {
|
||||||
return &streamError{Err: writeErr, StatusCode: 0, IsClientDisconnection: true}
|
return &streamError{Err: writeErr, StatusCode: 0, IsClientDisconnection: true}
|
||||||
}
|
}
|
||||||
return writeErr
|
// Headers already sent, can't send HTTP error response
|
||||||
|
return &streamError{Err: writeErr, StatusCode: 0, IsClientDisconnection: false}
|
||||||
}
|
}
|
||||||
flusher.Flush()
|
flusher.Flush()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -463,16 +463,6 @@ func (h *Handler) handleGet(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this will be a range request before streaming
|
|
||||||
isRangeRequest := r.Header.Get("Range") != ""
|
|
||||||
|
|
||||||
// Write status headers before streaming starts
|
|
||||||
if isRangeRequest {
|
|
||||||
w.WriteHeader(http.StatusPartialContent)
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := file.StreamResponse(w, r); err != nil {
|
if err := file.StreamResponse(w, r); err != nil {
|
||||||
var streamErr *streamError
|
var streamErr *streamError
|
||||||
if errors.As(err, &streamErr) {
|
if errors.As(err, &streamErr) {
|
||||||
@@ -480,12 +470,17 @@ func (h *Handler) handleGet(w http.ResponseWriter, r *http.Request) {
|
|||||||
if errors.Is(streamErr.Err, context.Canceled) || errors.Is(streamErr.Err, context.DeadlineExceeded) || streamErr.IsClientDisconnection {
|
if errors.Is(streamErr.Err, context.Canceled) || errors.Is(streamErr.Err, context.DeadlineExceeded) || streamErr.IsClientDisconnection {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// For other errors, we can't send HTTP error response since headers are already written
|
if streamErr.StatusCode > 0 {
|
||||||
h.logger.Error().Err(streamErr.Err).Str("file", file.name).Msg("Stream error after headers written")
|
http.Error(w, streamErr.Error(), streamErr.StatusCode)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
// We've already written a status code, just log the error
|
||||||
|
h.logger.Error().Err(streamErr.Err).Msg("Streaming error")
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Generic error - can't send HTTP error response since headers are already written
|
// Generic error
|
||||||
h.logger.Error().Err(err).Str("file", file.name).Msg("Generic stream error after headers written")
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user