func (w *response) declareTrailer(k string) {
	k = CanonicalHeaderKey(k)
	if !httpguts.ValidTrailerHeader(k) {
		return
	}
	w.trailers = append(w.trailers, k)
}
declareTrailer is called for each Trailer header when the
response header is written. It notes that a header will need to be
written in the trailers at the end of the response.
package http
import (
	"bufio"
	"bytes"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/textproto"
	"net/url"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"golang.org/x/net/http/httpguts"
)
var (
	ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
	ErrHijacked = errors.New("http: connection has been hijacked")
	ErrContentLength = errors.New("http: wrote more than the declared Content-Length")
	ErrWriteAfterFlush = errors.New("unused")
)
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
type ResponseWriter interface {
	Header() Header
	Write([]byte) (int, error)
	WriteHeader(statusCode int)
}
type Flusher interface {
	Flush()
}
type Hijacker interface {
	Hijack() (net.Conn, *bufio.ReadWriter, error)
}
type CloseNotifier interface {
	CloseNotify() <-chan bool
}
var (
	ServerContextKey = &contextKey{"http-server"}
	LocalAddrContextKey = &contextKey{"local-addr"}
)
type conn struct {
	server *Server
	cancelCtx context.CancelFunc
	rwc net.Conn
	remoteAddr string
	tlsState *tls.ConnectionState
	werr error
	r *connReader
	bufr *bufio.Reader
	bufw *bufio.Writer
	lastMethod string
	curReq atomic.Value  
	curState struct{ atomic uint64 }  
	mu sync.Mutex
	hijackedv bool
}
func (c *conn) hijacked() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.hijackedv
}
func (c *conn) hijackLocked() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
	if c.hijackedv {
		return nil, nil, ErrHijacked
	}
	c.r.abortPendingRead()
	c.hijackedv = true
	rwc = c.rwc
	rwc.SetDeadline(time.Time{})
	buf = bufio.NewReadWriter(c.bufr, bufio.NewWriter(rwc))
	if c.r.hasByte {
		if _, err := c.bufr.Peek(c.bufr.Buffered() + 1); err != nil {
			return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", err)
		}
	}
	c.setState(rwc, StateHijacked)
	return
}
const bufferBeforeChunkingSize = 2048
type chunkWriter struct {
	res *response
	header Header
	wroteHeader bool
	chunking bool  
}
var (
	crlf       = []byte("\r\n")
	colonSpace = []byte(": ")
)
func (cw *chunkWriter) Write(p []byte) (n int, err error) {
	if !cw.wroteHeader {
		cw.writeHeader(p)
	}
	if cw.res.req.Method == "HEAD" {
		return len(p), nil
	}
	if cw.chunking {
		_, err = fmt.Fprintf(cw.res.conn.bufw, "%x\r\n", len(p))
		if err != nil {
			cw.res.conn.rwc.Close()
			return
		}
	}
	n, err = cw.res.conn.bufw.Write(p)
	if cw.chunking && err == nil {
		_, err = cw.res.conn.bufw.Write(crlf)
	}
	if err != nil {
		cw.res.conn.rwc.Close()
	}
	return
}
func (cw *chunkWriter) flush() {
	if !cw.wroteHeader {
		cw.writeHeader(nil)
	}
	cw.res.conn.bufw.Flush()
}
func (cw *chunkWriter) close() {
	if !cw.wroteHeader {
		cw.writeHeader(nil)
	}
	if cw.chunking {
		bw := cw.res.conn.bufw  
		bw.WriteString("0\r\n")
		if trailers := cw.res.finalTrailers(); trailers != nil {
			trailers.Write(bw)  
		}
		bw.WriteString("\r\n")
	}
}
type response struct {
	conn             *conn
	req              *Request  
	reqBody          io.ReadCloser
	cancelCtx        context.CancelFunc  
	wroteHeader      bool                
	wroteContinue    bool                
	wants10KeepAlive bool                
	wantsClose       bool                
	w  *bufio.Writer  
	cw chunkWriter
	handlerHeader Header
	calledHeader  bool  
	written       int64  
	contentLength int64  
	status        int    
	closeAfterReply bool
	requestBodyLimitHit bool
	trailers []string
	handlerDone atomicBool  
	dateBuf   [len(TimeFormat)]byte
	clenBuf   [10]byte
	statusBuf [3]byte
	closeNotifyCh  chan bool
	didCloseNotify int32  
}
const TrailerPrefix = "Trailer:"
func (w *response) finalTrailers() Header {
	var t Header
	for k, vv := range w.handlerHeader {
		if strings.HasPrefix(k, TrailerPrefix) {
			if t == nil {
				t = make(Header)
			}
			t[strings.TrimPrefix(k, TrailerPrefix)] = vv
		}
	}
	for _, k := range w.trailers {
		if t == nil {
			t = make(Header)
		}
		for _, v := range w.handlerHeader[k] {
			t.Add(k, v)
		}
	}
	return t
}
type atomicBool int32
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) }