I am trying to use gin to construct a stream server supporting http-flv. As expected, if the header "Content-Length" is not set, the connection will be kept open. However, whenever I invoke the method context.Writer.Write([]byte), I get the error mentioned above. I refered to the comment attached in file server.go, which claims "Additionally, if the total size of all written data is under a few KB and there are no Flush calls, the Content-Length header is added automatically." func NewFlvWriter(ctx gin.Context, parent flvMuxer) (*FlvWriter, error) { // TODO: 填充某些字段 writer := &FlvWriter{ remoteAddr: ctx.RemoteIP(), streamName: parent.pathName, buf: make([]byte, 11), parent: parent, ctx: ctx, }
chPacketFlvWriter := make(chan *flvPacket, 64)
ctx.Header("Transfer-Encoding", "chunked")
ctx.Header("Content-Type", "application/octet-stream")
//ctx.Header("Content-Length", "-1")
ctx.Writer.WriteHeader(200)
_, err := ctx.Writer.Write(flvHeader)
if err != nil {
writer.Log(logger.Error, "error writing flv header: "+err.Error()+", content length: "+ctx.Writer.Header().Get("Content-Length"))
return nil, err
}
pio.PutI32BE(writer.buf[:4], 0)
_, err = ctx.Writer.Write(writer.buf[:4])
if err != nil {
writer.Log(logger.Error, "error writing blanks: "+err.Error())
return nil, err
}
// TODO: 记得在Writer结束之前销毁里面的对应的kv
parent.packetBuffer.Store(ctx, chPacketFlvWriter)
return writer, nil
}
Comment From: kaylee595
I can't reproduce your problem, you can try to set a breakpoint at line 1177 of server.go to try to see the value of v/cl.
Please set a breakpoint at w.contentLength = v
below for debugging
// server.go
func (w *response) WriteHeader(code int) {
if w.conn.hijacked() {
caller := relevantCaller()
w.conn.server.logf("http: response.WriteHeader on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)
return
}
if w.wroteHeader {
caller := relevantCaller()
w.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)
return
}
checkWriteHeaderCode(code)
// Handle informational headers
if code >= 100 && code <= 199 {
// Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read()
if code == 100 && w.canWriteContinue.Load() {
w.writeContinueMu.Lock()
w.canWriteContinue.Store(false)
w.writeContinueMu.Unlock()
}
writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:])
// Per RFC 8297 we must not clear the current header map
w.handlerHeader.WriteSubset(w.conn.bufw, excludedHeadersNoBody)
w.conn.bufw.Write(crlf)
w.conn.bufw.Flush()
return
}
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.Clone()
}
if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
w.conn.server.logf("http: invalid Content-Length of %q", cl)
w.handlerHeader.Del("Content-Length")
}
}
}