timeout icon indicating copy to clipboard operation
timeout copied to clipboard

when i download excel, timeout middleware lead to response to failed

Open gaozhenyusky opened this issue 2 years ago • 0 comments

  • With issues:
    • Use the search tool before opening a new issue.
    • Please provide source code and commit sha if you found a bug.
    • Review existing issues and provide feedback or react to them.

Description

I use gin and reverse_proxy to make a gateway, as well i use gin-contrib/timeout to Preventing Gateway timeouts.

but when i request to download .xls file, it show me the debug log: '[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 404 with 200'

if i not use gin-contrib/timeout , it`s download excel ok

How to reproduce

I found this code tw.ResponseWriter.WriteHeader(tw.code) will debug log

package middleware

import (
	"github.com/gin-gonic/gin"
)

select {
		case p := <-panicChan:
			tw.FreeBuffer()
			c.Writer = w
			panic(p)

		case <-finish:
			c.Next()
			tw.mu.Lock()
			defer tw.mu.Unlock()
			dst := tw.ResponseWriter.Header()
			for k, vv := range tw.Header() {
				dst[k] = vv
			}
			tw.ResponseWriter.WriteHeader(tw.code)
			if _, err := tw.ResponseWriter.Write(buffer.Bytes()); err != nil {
				panic(err)
			}
			tw.FreeBuffer()
			bufPool.Put(buffer)

		case <-time.After(t.timeout):
			c.Abort()
			tw.mu.Lock()
			defer tw.mu.Unlock()
			tw.timeout = true
			tw.FreeBuffer()
			bufPool.Put(buffer)

			c.Writer = w
			t.response(c)
			c.Writer = tw
		}

And when i download excel file , it flushed, and use func WriteHeaderNow to make w.size = 0, it lead to my request failed with 404 statusCode

package gin

import (
	"bufio"
	"io"
	"net"
	"net/http"
)

const (
	noWritten     = -1
	defaultStatus = http.StatusOK
)

func (w *responseWriter) WriteHeader(code int) {
	if code > 0 && w.status != code {
		if w.Written() {
			debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
		}
		w.status = code
	}
}

func (w *responseWriter) Written() bool {
	return w.size != noWritten
}

func (w *responseWriter) Flush() {
	w.WriteHeaderNow()
	w.ResponseWriter.(http.Flusher).Flush()
}

func (w *responseWriter) WriteHeaderNow() {
	if !w.Written() {
		w.size = 0
		w.ResponseWriter.WriteHeader(w.status)
	}
}

Expectations

i hope someone can teach me how to solve this problem, thx )

Environment

  • go version: 1.19
  • gin version (or commit ref): 1.8.1
  • operating system: macOS 12.6 arm

gaozhenyusky avatar Nov 23 '22 07:11 gaozhenyusky