Source File
server.go
Belonging Package
net/http
package http
import (
urlpkg
)
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)
}
Header() Header
WriteHeader(statusCode int)
}
Flush()
}
Hijack() (net.Conn, *bufio.ReadWriter, error)
}
CloseNotify() <-chan bool
}
ServerContextKey = &contextKey{"http-server"}
LocalAddrContextKey = &contextKey{"local-addr"}
)
func ( *conn) () ( net.Conn, *bufio.ReadWriter, error) {
if .hijackedv {
return nil, nil, ErrHijacked
}
.r.abortPendingRead()
.hijackedv = true
= .rwc
.SetDeadline(time.Time{})
= bufio.NewReadWriter(.bufr, bufio.NewWriter())
if .r.hasByte {
if , := .bufr.Peek(.bufr.Buffered() + 1); != nil {
return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", )
}
}
.setState(, StateHijacked, runHooks)
return
}
const bufferBeforeChunkingSize = 2048
type chunkWriter struct {
res *response
chunking bool // using chunked transfer encoding for reply body
}
var (
crlf = []byte("\r\n")
colonSpace = []byte(": ")
)
func ( *chunkWriter) ( []byte) ( int, error) {
if !.wroteHeader {
.writeHeader()
}
return len(), nil
}
if .chunking {
_, = fmt.Fprintf(.res.conn.bufw, "%x\r\n", len())
if != nil {
.res.conn.rwc.Close()
return
}
}
, = .res.conn.bufw.Write()
if .chunking && == nil {
_, = .res.conn.bufw.Write(crlf)
}
if != nil {
.res.conn.rwc.Close()
}
return
}
func ( *chunkWriter) () {
if !.wroteHeader {
.writeHeader(nil)
}
.res.conn.bufw.Flush()
}
func ( *chunkWriter) () {
if !.wroteHeader {
.writeHeader(nil)
}
if .chunking {
.WriteString("0\r\n")
if := .res.finalTrailers(); != nil {
.Write() // the writer handles noting errors
.WriteString("\r\n")
}
}
type response struct {
conn *conn
req *Request // request for this response
reqBody io.ReadCloser
cancelCtx context.CancelFunc // when ServeHTTP exits
wroteHeader bool // reply header has been (logically) written
wroteContinue bool // 100 Continue response was written
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
wantsClose bool // HTTP request has Connection "close"
canWriteContinue atomicBool
writeContinueMu sync.Mutex
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
handlerHeader Header
calledHeader bool // handler accessed handlerHeader via Header
written int64 // number of bytes written in body
contentLength int64 // explicitly-declared Content-Length; or -1
status int // status code passed to WriteHeader
trailers []string
handlerDone atomicBool // set true when the handler exits
closeNotifyCh chan bool
didCloseNotify int32 // atomic (only 0->1 winner should send)
}
const TrailerPrefix = "Trailer:"
func ( *response) () Header {
var Header
for , := range .handlerHeader {
if strings.HasPrefix(, TrailerPrefix) {
if == nil {
= make(Header)
}
[strings.TrimPrefix(, TrailerPrefix)] =
}
}
for , := range .trailers {
if == nil {
= make(Header)
}
for , := range .handlerHeader[] {
.Add(, )
}
}
return
}
type atomicBool int32
func ( *atomicBool) () bool { return atomic.LoadInt32((*int32)()) != 0 }
func ( *atomicBool) () { atomic.StoreInt32((*int32)(), 1) }
func ( *atomicBool) () { atomic.StoreInt32((*int32)(), 0) }
func ( *response) ( string) {
= CanonicalHeaderKey()
func ( *response) () {
.closeAfterReply = true
.requestBodyLimitHit = true
if !.wroteHeader {
.Header().Set("Connection", "close")
}
}
func ( *response) () bool {
, := .handlerHeader["Content-Type"]
return !.cw.wroteHeader && ! && .written < sniffLen
}
type writerOnly struct {
io.Writer
}
func ( *response) ( io.Reader) ( int64, error) {
:= copyBufPool.Get().(*[]byte)
:= *
defer copyBufPool.Put()
, := .conn.rwc.(io.ReaderFrom)
if ! {
return io.CopyBuffer(writerOnly{}, , )
}
if == nil && !.wroteHeader {
.WriteHeader(StatusOK) // nr == 0, no error (or EOF)
}
if != nil || {
return ,
}
.w.Flush() // get rid of any previous writes
.cw.flush() // make sure Header is written; flush data to rwc
if !.cw.chunking && .bodyAllowed() {
, := .ReadFrom()
+=
.written +=
return ,
}
, := io.Copy(writerOnly{}, )
+=
return ,
}
const debugServerConnections = false
func ( *Server) ( net.Conn) *conn {
:= &conn{
server: ,
rwc: ,
}
if debugServerConnections {
.rwc = newLoggingConn("server", .rwc)
}
return
}
type readResult struct {
_ incomparable
n int
err error
b byte // byte read, if n == 1
}
type connReader struct {
conn *conn
mu sync.Mutex // guards following
hasByte bool
byteBuf [1]byte
cond *sync.Cond
inRead bool
aborted bool // set true before conn.rwc deadline is set to past
remain int64 // bytes remaining
}
func ( *connReader) () {
.mu.Lock()
if .cond == nil {
.cond = sync.NewCond(&.mu)
}
}
func ( *connReader) () { .mu.Unlock() }
func ( *connReader) () {
.lock()
defer .unlock()
if .inRead {
panic("invalid concurrent Body.Read call")
}
if .hasByte {
return
}
.inRead = true
.conn.rwc.SetReadDeadline(time.Time{})
go .backgroundRead()
}
func ( *connReader) () {
, := .conn.rwc.Read(.byteBuf[:])
.lock()
if == 1 {
}
} else if != nil {
.handleReadError()
}
.aborted = false
.inRead = false
.unlock()
.cond.Broadcast()
}
func ( *connReader) () {
.lock()
defer .unlock()
if !.inRead {
return
}
.aborted = true
.conn.rwc.SetReadDeadline(aLongTimeAgo)
for .inRead {
.cond.Wait()
}
.conn.rwc.SetReadDeadline(time.Time{})
}
func ( *connReader) ( int64) { .remain = }
func ( *connReader) () { .remain = maxInt64 }
func ( *connReader) () bool { return .remain <= 0 }
func ( *connReader) ( error) {
.conn.cancelCtx()
.closeNotify()
}
func ( *connReader) () {
, := .conn.curReq.Load().(*response)
if != nil && atomic.CompareAndSwapInt32(&.didCloseNotify, 0, 1) {
.closeNotifyCh <- true
}
}
func ( *connReader) ( []byte) ( int, error) {
.lock()
if .inRead {
.unlock()
if .conn.hijacked() {
panic("invalid Body.Read call. After hijacked, the original Request must not be used")
}
panic("invalid concurrent Body.Read call")
}
if .hitReadLimit() {
.unlock()
return 0, io.EOF
}
if len() == 0 {
.unlock()
return 0, nil
}
if int64(len()) > .remain {
= [:.remain]
}
if .hasByte {
[0] = .byteBuf[0]
.hasByte = false
.unlock()
return 1, nil
}
.inRead = true
.unlock()
, = .conn.rwc.Read()
.lock()
.inRead = false
if != nil {
.handleReadError()
}
.remain -= int64()
.unlock()
.cond.Broadcast()
return ,
}
var (
bufioReaderPool sync.Pool
bufioWriter2kPool sync.Pool
bufioWriter4kPool sync.Pool
)
var copyBufPool = sync.Pool{
New: func() interface{} {
:= make([]byte, 32*1024)
return &
},
}
func ( int) *sync.Pool {
switch {
case 2 << 10:
return &bufioWriter2kPool
case 4 << 10:
return &bufioWriter4kPool
}
return nil
}
func ( io.Reader) *bufio.Reader {
if := bufioReaderPool.Get(); != nil {
:= .(*bufio.Reader)
.Reset()
return
return bufio.NewReader()
}
func ( *bufio.Reader) {
.Reset(nil)
bufioReaderPool.Put()
}
func ( io.Writer, int) *bufio.Writer {
:= bufioWriterPool()
if != nil {
if := .Get(); != nil {
:= .(*bufio.Writer)
.Reset()
return
}
}
return bufio.NewWriterSize(, )
}
func ( *bufio.Writer) {
.Reset(nil)
if := bufioWriterPool(.Available()); != nil {
.Put()
}
}
const DefaultMaxHeaderBytes = 1 << 20 // 1 MB
func ( *Server) () int {
if .MaxHeaderBytes > 0 {
return .MaxHeaderBytes
}
return DefaultMaxHeaderBytes
}
func ( *Server) () int64 {
return int64(.maxHeaderBytes()) + 4096 // bufio slop
}
type expectContinueReader struct {
resp *response
readCloser io.ReadCloser
closed atomicBool
sawEOF atomicBool
}
func ( *expectContinueReader) ( []byte) ( int, error) {
if .closed.isSet() {
return 0, ErrBodyReadAfterClose
}
:= .resp
if !.wroteContinue && .canWriteContinue.isSet() && !.conn.hijacked() {
.wroteContinue = true
.writeContinueMu.Lock()
if .canWriteContinue.isSet() {
.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
.conn.bufw.Flush()
.canWriteContinue.setFalse()
}
.writeContinueMu.Unlock()
}
, = .readCloser.Read()
if == io.EOF {
.sawEOF.setTrue()
}
return
}
func ( *expectContinueReader) () error {
.closed.setTrue()
return .readCloser.Close()
}
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
func ( []byte, time.Time) []byte {
const = "SunMonTueWedThuFriSat"
const = "JanFebMarAprMayJunJulAugSepOctNovDec"
= .UTC()
, , := .Date()
, , := .Clock()
:= [3*.Weekday():]
:= [3*(-1):]
return append(,
[0], [1], [2], ',', ' ',
byte('0'+/10), byte('0'+%10), ' ',
[0], [1], [2], ' ',
byte('0'+/1000), byte('0'+(/100)%10), byte('0'+(/10)%10), byte('0'+%10), ' ',
byte('0'+/10), byte('0'+%10), ':',
byte('0'+/10), byte('0'+%10), ':',
byte('0'+/10), byte('0'+%10), ' ',
'G', 'M', 'T')
}
var errTooLarge = errors.New("http: request too large")
func ( *conn) ( context.Context) ( *response, error) {
if .hijacked() {
return nil, ErrHijacked
}
var (
time.Time // or zero if none
time.Time // or zero if none
)
:= time.Now()
if := .server.readHeaderTimeout(); != 0 {
= .Add()
}
if := .server.ReadTimeout; != 0 {
= .Add()
}
.rwc.SetReadDeadline()
if := .server.WriteTimeout; != 0 {
defer func() {
.rwc.SetWriteDeadline(time.Now().Add())
}()
}
.r.setReadLimit(.server.initialReadLimitSize())
, := .bufr.Peek(4) // ReadRequest will get err below
.bufr.Discard(numLeadingCRorLF())
}
, := readRequest(.bufr, keepHostHeader)
if != nil {
if .r.hitReadLimit() {
return nil, errTooLarge
}
return nil,
}
if !http1ServerSupportsRequest() {
return nil, statusError{StatusHTTPVersionNotSupported, "unsupported protocol version"}
}
.lastMethod = .Method
.r.setInfiniteReadLimit()
, := .Header["Host"]
:= .isH2Upgrade()
if .ProtoAtLeast(1, 1) && (! || len() == 0) && ! && .Method != "CONNECT" {
return nil, badRequestError("missing required Host header")
}
if len() > 1 {
return nil, badRequestError("too many Host headers")
}
if len() == 1 && !httpguts.ValidHostHeader([0]) {
return nil, badRequestError("malformed Host header")
}
for , := range .Header {
if !httpguts.ValidHeaderFieldName() {
return nil, badRequestError("invalid header name")
}
for , := range {
if !httpguts.ValidHeaderFieldValue() {
return nil, badRequestError("invalid header value")
}
}
}
delete(.Header, "Host")
, := context.WithCancel()
.ctx =
.RemoteAddr = .remoteAddr
.TLS = .tlsState
if , := .Body.(*body); {
.doEarlyClose = true
}
if !.Equal() {
.rwc.SetReadDeadline()
}
= &response{
conn: ,
cancelCtx: ,
req: ,
reqBody: .Body,
handlerHeader: make(Header),
contentLength: -1,
closeNotifyCh: make(chan bool, 1),
wants10KeepAlive: .wantsHttp10KeepAlive(),
wantsClose: .wantsClose(),
}
if {
.closeAfterReply = true
}
.cw.res =
.w = newBufioWriterSize(&.cw, bufferBeforeChunkingSize)
return , nil
}
func ( *Request) bool {
if .ProtoMajor == 1 {
return true
if .ProtoMajor == 2 && .ProtoMinor == 0 &&
.Method == "PRI" && .RequestURI == "*" {
return true
.cw.header = .handlerHeader.Clone()
}
.calledHeader = true
return .handlerHeader
}
const maxPostHandlerReadBytes = 256 << 10
func () runtime.Frame {
:= make([]uintptr, 16)
:= runtime.Callers(1, )
:= runtime.CallersFrames([:])
var runtime.Frame
for {
, := .Next()
if !strings.HasPrefix(.Function, "net/http.") {
return
}
if ! {
break
}
}
return
}
func ( *response) ( int) {
if .conn.hijacked() {
:= relevantCaller()
.conn.server.logf("http: response.WriteHeader on hijacked connection from %s (%s:%d)", .Function, path.Base(.File), .Line)
return
}
if .wroteHeader {
:= relevantCaller()
.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", .Function, path.Base(.File), .Line)
return
}
checkWriteHeaderCode()
.wroteHeader = true
.status =
if .calledHeader && .cw.header == nil {
.cw.header = .handlerHeader.Clone()
}
if := .handlerHeader.get("Content-Length"); != "" {
, := strconv.ParseInt(, 10, 64)
if == nil && >= 0 {
.contentLength =
} else {
.conn.server.logf("http: invalid Content-Length of %q", )
.handlerHeader.Del("Content-Length")
}
}
}
type extraHeader struct {
contentType string
connection string
transferEncoding string
date []byte // written if not nil
contentLength []byte // written if not nil
}
var extraHeaderKeys = [][]byte{
[]byte("Content-Type"),
[]byte("Connection"),
[]byte("Transfer-Encoding"),
}
var (
headerContentLength = []byte("Content-Length: ")
headerDate = []byte("Date: ")
)
func ( extraHeader) ( *bufio.Writer) {
if .date != nil {
.Write(headerDate)
.Write(.date)
.Write(crlf)
}
if .contentLength != nil {
.Write(headerContentLength)
.Write(.contentLength)
.Write(crlf)
}
for , := range []string{.contentType, .connection, .transferEncoding} {
if != "" {
.Write(extraHeaderKeys[])
.Write(colonSpace)
.WriteString()
.Write(crlf)
}
}
}
func ( *chunkWriter) ( []byte) {
if .wroteHeader {
return
}
.wroteHeader = true
:= .res
:= .conn.server.doKeepAlives()
:= .req.Method == "HEAD"
if .handlerDone.isSet() && ! && ! && bodyAllowedForStatus(.status) && .get("Content-Length") == "" && (! || len() > 0) {
.contentLength = int64(len())
.contentLength = strconv.AppendInt(.res.clenBuf[:0], int64(len()), 10)
}
if .wants10KeepAlive && {
:= .get("Content-Length") != ""
if && .get("Connection") == "keep-alive" {
.closeAfterReply = false
}
}
:= .contentLength != -1
if .wants10KeepAlive && ( || || !bodyAllowedForStatus(.status)) {
, := ["Connection"]
if ! {
.connection = "keep-alive"
}
} else if !.req.ProtoAtLeast(1, 1) || .wantsClose {
.closeAfterReply = true
}
if .get("Connection") == "close" || ! {
.closeAfterReply = true
}
if , := .req.Body.(*expectContinueReader); && !.sawEOF.isSet() {
.closeAfterReply = true
}
if .req.ContentLength != 0 && !.closeAfterReply {
var , bool
switch bdy := .req.Body.(type) {
case *expectContinueReader:
if .resp.wroteContinue {
= true
}
case *body:
.mu.Lock()
switch {
case .closed:
.closeAfterReply = true
}
case .unreadDataSizeLocked() >= maxPostHandlerReadBytes:
= true
default:
= true
}
.mu.Unlock()
default:
= true
}
if {
, := io.CopyN(io.Discard, .reqBody, maxPostHandlerReadBytes+1)
switch {
= true
= .reqBody.Close()
if != nil {
.closeAfterReply = true
}
.closeAfterReply = true
}
}
if {
.requestTooLarge()
("Connection")
.connection = "close"
}
}
:= .status
, := ["Content-Type"]
:= .Get("Content-Encoding")
:= len() > 0
if ! && ! && ! && len() > 0 {
.contentType = DetectContentType()
}
} else {
for , := range suppressedHeaders() {
()
}
}
if !.has("Date") {
.date = appendTime(.res.dateBuf[:0], time.Now())
}
.conn.server.logf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
, .contentLength)
("Content-Length")
= false
}
} else if == StatusNoContent {
("Transfer-Encoding")
} else if {
("Transfer-Encoding")
if && == "identity" {
.chunking = false
.closeAfterReply = true
.chunking = true
.transferEncoding = "chunked"
("Transfer-Encoding")
}
}
.closeAfterReply = true
("Transfer-Encoding") // in case already set
}
if .chunking {
("Content-Length")
}
if !.req.ProtoAtLeast(1, 0) {
return
}
:= .closeAfterReply &&
(! || !hasToken(.header.get("Connection"), "close")) &&
!isProtocolSwitchResponse(.status, )
if {
("Connection")
if .req.ProtoAtLeast(1, 1) {
.connection = "close"
}
}
writeStatusLine(.conn.bufw, .req.ProtoAtLeast(1, 1), , .statusBuf[:])
.header.WriteSubset(.conn.bufw, )
.Write(.conn.bufw)
.conn.bufw.Write(crlf)
}
func ( string, func(string)) {
= textproto.TrimString()
if == "" {
return
}
if !strings.Contains(, ",") {
()
return
}
for , := range strings.Split(, ",") {
if = textproto.TrimString(); != "" {
()
}
}
}
func ( *bufio.Writer, bool, int, []byte) {
if {
.WriteString("HTTP/1.1 ")
} else {
.WriteString("HTTP/1.0 ")
}
if , := statusText[]; {
.Write(strconv.AppendInt([:0], int64(), 10))
.WriteByte(' ')
.WriteString()
.WriteString("\r\n")
func ( *response) () bool {
if !.wroteHeader {
panic("")
}
return bodyAllowedForStatus(.status)
}
.writeContinueMu.Lock()
.canWriteContinue.setFalse()
.writeContinueMu.Unlock()
}
if !.wroteHeader {
.WriteHeader(StatusOK)
}
if == 0 {
return 0, nil
}
if !.bodyAllowed() {
return 0, ErrBodyNotAllowed
}
.written += int64() // ignoring errors, for errorKludge
if .contentLength != -1 && .written > .contentLength {
return 0, ErrContentLength
}
if != nil {
return .w.Write()
} else {
return .w.WriteString()
}
}
func ( *response) () {
.handlerDone.setTrue()
if !.wroteHeader {
.WriteHeader(StatusOK)
}
.w.Flush()
putBufioWriter(.w)
.cw.close()
.conn.bufw.Flush()
.conn.r.abortPendingRead()
.reqBody.Close()
if .req.MultipartForm != nil {
.req.MultipartForm.RemoveAll()
}
}
return false
}
return false
}
putBufioWriter(.bufw)
.bufw = nil
}
}
func ( *conn) () {
.finalFlush()
.rwc.Close()
}
const rstAvoidanceDelay = 500 * time.Millisecond
type closeWriter interface {
CloseWrite() error
}
var _ closeWriter = (*net.TCPConn)(nil)
func ( *conn) () {
.finalFlush()
if , := .rwc.(closeWriter); {
.CloseWrite()
}
time.Sleep(rstAvoidanceDelay)
}
func ( string) bool {
switch {
case "", "http/1.1", "http/1.0":
return false
}
return true
}
const (
runHooks = true
skipHooks = false
)
func ( *conn) ( net.Conn, ConnState, bool) {
:= .server
switch {
case StateNew:
.trackConn(, true)
case StateHijacked, StateClosed:
.trackConn(, false)
}
if > 0xff || < 0 {
panic("internal error")
}
:= uint64(time.Now().Unix()<<8) | uint64()
atomic.StoreUint64(&.curState.atomic, )
if ! {
return
}
if := .ConnState; != nil {
(, )
}
}
func ( *conn) () ( ConnState, int64) {
:= atomic.LoadUint64(&.curState.atomic)
return ConnState( & 0xff), int64( >> 8)
}
func ( string) error { return statusError{StatusBadRequest, } }
type statusError struct {
code int
text string
}
func ( statusError) () string { return StatusText(.code) + ": " + .text }
var ErrAbortHandler = errors.New("net/http: abort Handler")
func ( *conn) ( context.Context) {
.remoteAddr = .rwc.RemoteAddr().String()
= context.WithValue(, LocalAddrContextKey, .rwc.LocalAddr())
defer func() {
if := recover(); != nil && != ErrAbortHandler {
const = 64 << 10
:= make([]byte, )
= [:runtime.Stack(, false)]
.server.logf("http: panic serving %v: %v\n%s", .remoteAddr, , )
}
if !.hijacked() {
.close()
.setState(.rwc, StateClosed, runHooks)
}
}()
if , := .rwc.(*tls.Conn); {
if := .server.ReadTimeout; != 0 {
.rwc.SetReadDeadline(time.Now().Add())
}
if := .server.WriteTimeout; != 0 {
.rwc.SetWriteDeadline(time.Now().Add())
}
if , := .(tls.RecordHeaderError); && .Conn != nil && tlsRecordHeaderLooksLikeHTTP(.RecordHeader) {
io.WriteString(.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
.Conn.Close()
return
}
.server.logf("http: TLS handshake error from %s: %v", .rwc.RemoteAddr(), )
return
}
.tlsState = new(tls.ConnectionState)
*.tlsState = .ConnectionState()
if := .tlsState.NegotiatedProtocol; validNextProto() {
if := .server.TLSNextProto[]; != nil {
.setState(.rwc, StateActive, skipHooks)
(.server, , )
}
return
}
}
, := context.WithCancel()
.cancelCtx =
defer ()
.r = &connReader{conn: }
.bufr = newBufioReader(.r)
.bufw = newBufioWriterSize(checkConnErrorWriter{}, 4<<10)
for {
, := .readRequest()
.setState(.rwc, StateActive, runHooks)
}
if != nil {
const = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
switch {
const = "431 Request Header Fields Too Large"
fmt.Fprintf(.rwc, "HTTP/1.1 "+++)
.closeWriteAndWait()
return
fmt.Fprintf(.rwc, "HTTP/1.1 %d %s%sUnsupported transfer encoding", , StatusText(), )
return
case isCommonNetReadError():
return // don't reply
default:
if , := .(statusError); {
fmt.Fprintf(.rwc, "HTTP/1.1 %d %s: %s%s%d %s: %s", .code, StatusText(.code), .text, , .code, StatusText(.code), .text)
return
}
:= "400 Bad Request"
fmt.Fprintf(.rwc, "HTTP/1.1 "+++)
return
}
}
:= .req
if .expectsContinue() {
.Body = &expectContinueReader{readCloser: .Body, resp: }
.canWriteContinue.setTrue()
}
} else if .Header.get("Expect") != "" {
.sendExpectationFailed()
return
}
.curReq.Store()
if requestBodyRemains(.Body) {
registerOnHitEOF(.Body, .conn.r.startBackgroundRead)
} else {
.conn.r.startBackgroundRead()
}
serverHandler{.server}.ServeHTTP(, .req)
.cancelCtx()
if .hijacked() {
return
}
.finishRequest()
if !.shouldReuseConnection() {
if .requestBodyLimitHit || .closedRequestBodyEarly() {
.closeWriteAndWait()
}
return
}
.setState(.rwc, StateIdle, runHooks)
.curReq.Store((*response)(nil))
return
}
if := .server.idleTimeout(); != 0 {
.rwc.SetReadDeadline(time.Now().Add())
if , := .bufr.Peek(4); != nil {
return
}
}
.rwc.SetReadDeadline(time.Time{})
}
}
.Header().Set("Connection", "close")
.WriteHeader(StatusExpectationFailed)
.finishRequest()
}
, , = .hijackLocked()
if == nil {
putBufioWriter(.w)
.w = nil
}
return , ,
}
func ( *response) () <-chan bool {
if .handlerDone.isSet() {
panic("net/http: CloseNotify called after ServeHTTP finished")
}
return .closeNotifyCh
}
func ( io.ReadCloser, func()) {
switch v := .(type) {
case *expectContinueReader:
(.readCloser, )
case *body:
.registerOnHitEOF()
default:
panic("unexpected type " + fmt.Sprintf("%T", ))
}
}
func ( io.ReadCloser) bool {
if == NoBody {
return false
}
switch v := .(type) {
case *expectContinueReader:
return (.readCloser)
case *body:
return .bodyRemains()
default:
panic("unexpected type " + fmt.Sprintf("%T", ))
}
}
type HandlerFunc func(ResponseWriter, *Request)
func ( HandlerFunc) ( ResponseWriter, *Request) {
(, )
}
func ( ResponseWriter, string, int) {
.Header().Set("Content-Type", "text/plain; charset=utf-8")
.Header().Set("X-Content-Type-Options", "nosniff")
.WriteHeader()
fmt.Fprintln(, )
}
func ( ResponseWriter, *Request) { Error(, "404 page not found", StatusNotFound) }
func () Handler { return HandlerFunc(NotFound) }
func ( string, Handler) Handler {
if == "" {
return
}
return HandlerFunc(func( ResponseWriter, *Request) {
:= strings.TrimPrefix(.URL.Path, )
:= strings.TrimPrefix(.URL.RawPath, )
if len() < len(.URL.Path) && (.URL.RawPath == "" || len() < len(.URL.RawPath)) {
:= new(Request)
* = *
.URL = new(url.URL)
*.URL = *.URL
.URL.Path =
.URL.RawPath =
.ServeHTTP(, )
} else {
NotFound(, )
}
})
}
func ( ResponseWriter, *Request, string, int) {
, := ["Content-Type"]
.Set("Location", hexEscapeNonASCII())
if ! && (.Method == "GET" || .Method == "HEAD") {
.Set("Content-Type", "text/html; charset=utf-8")
}
.WriteHeader()
if ! && .Method == "GET" {
:= "<a href=\"" + htmlEscape() + "\">" + statusText[] + "</a>.\n"
fmt.Fprintln(, )
}
}
var htmlReplacer = strings.NewReplacer(
"&", "&",
"<", "<",
"'", "'",
)
func ( string) string {
return htmlReplacer.Replace()
}
type redirectHandler struct {
url string
code int
}
func ( *redirectHandler) ( ResponseWriter, *Request) {
Redirect(, , .url, .code)
}
func ( string, int) Handler {
return &redirectHandler{, }
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
if strings.IndexByte(, ':') == -1 {
return
}
, , := net.SplitHostPort()
if != nil {
return // on error, return unchanged
}
return
}
if , := .redirectToPathSlash(.URL.Host, .URL.Path, .URL); {
return RedirectHandler(.String(), StatusMovedPermanently), .Path
}
return .handler(.Host, .URL.Path)
}
:= stripHostPort(.Host)
:= cleanPath(.URL.Path)
if , := .redirectToPathSlash(, , .URL); {
return RedirectHandler(.String(), StatusMovedPermanently), .Path
}
if != .URL.Path {
_, = .handler(, )
:= *.URL
.Path =
return RedirectHandler(.String(), StatusMovedPermanently),
}
return .handler(, .URL.Path)
}
func ( *ServeMux) ( ResponseWriter, *Request) {
if .RequestURI == "*" {
if .ProtoAtLeast(1, 1) {
.Header().Set("Connection", "close")
}
.WriteHeader(StatusBadRequest)
return
}
, := .Handler()
.ServeHTTP(, )
}
func ( *ServeMux) ( string, Handler) {
.mu.Lock()
defer .mu.Unlock()
if == "" {
panic("http: invalid pattern")
}
if == nil {
panic("http: nil handler")
}
if , := .m[]; {
panic("http: multiple registrations for " + )
}
if .m == nil {
.m = make(map[string]muxEntry)
}
:= muxEntry{h: , pattern: }
.m[] =
if [len()-1] == '/' {
.es = appendSorted(.es, )
}
if [0] != '/' {
.hosts = true
}
}
func ( []muxEntry, muxEntry) []muxEntry {
:= len()
:= sort.Search(, func( int) bool {
return len([].pattern) < len(.pattern)
})
if == {
return append(, )
func ( *ServeMux) ( string, func(ResponseWriter, *Request)) {
if == nil {
panic("http: nil handler")
}
.Handle(, HandlerFunc())
}
func ( string, Handler) { DefaultServeMux.Handle(, ) }
func ( string, func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(, )
}
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
inShutdown atomicBool // true when when server is in shutdown
disableKeepAlives int32 // accessed atomically.
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
doneChan chan struct{}
onShutdown []func()
}
func ( *Server) () <-chan struct{} {
.mu.Lock()
defer .mu.Unlock()
return .getDoneChanLocked()
}
func ( *Server) () chan struct{} {
if .doneChan == nil {
.doneChan = make(chan struct{})
}
return .doneChan
}
func ( *Server) () {
:= .getDoneChanLocked()
select {
close()
}
}
func ( *Server) () error {
.inShutdown.setTrue()
.mu.Lock()
defer .mu.Unlock()
.closeDoneChanLocked()
:= .closeListenersLocked()
for := range .activeConn {
.rwc.Close()
delete(.activeConn, )
}
return
}
const shutdownPollIntervalMax = 500 * time.Millisecond
func ( *Server) ( context.Context) error {
.inShutdown.setTrue()
.mu.Lock()
:= .closeListenersLocked()
.closeDoneChanLocked()
for , := range .onShutdown {
go ()
}
.mu.Unlock()
:= time.Millisecond
*= 2
if > shutdownPollIntervalMax {
= shutdownPollIntervalMax
}
return
}
:= time.NewTimer(())
defer .Stop()
for {
if .closeIdleConns() && .numListeners() == 0 {
return
}
select {
case <-.Done():
return .Err()
case <-.C:
.Reset(())
}
}
}
StateClosed
)
var stateName = map[ConnState]string{
StateNew: "new",
StateActive: "active",
StateIdle: "idle",
StateHijacked: "hijacked",
StateClosed: "closed",
}
func ( ConnState) () string {
return stateName[]
}
type serverHandler struct {
srv *Server
}
func ( serverHandler) ( ResponseWriter, *Request) {
:= .srv.Handler
if == nil {
= DefaultServeMux
}
if .RequestURI == "*" && .Method == "OPTIONS" {
= globalOptionsHandler{}
}
.ServeHTTP(, )
}
func ( *Server) () error {
if .shuttingDown() {
return ErrServerClosed
}
:= .Addr
if == "" {
= ":http"
}
, := net.Listen("tcp", )
if != nil {
return
}
return .Serve()
}
var testHookServerServe func(*Server, net.Listener) // used if non-nil
return true
return strSliceContains(.TLSConfig.NextProtos, http2NextProtoTLS)
}
var ErrServerClosed = errors.New("http: Server closed")
func ( *Server) ( net.Listener) error {
if := testHookServerServe; != nil {
(, ) // call hook with unwrapped listener
}
:=
= &onceCloseListener{Listener: }
defer .Close()
if := .setupHTTP2_Serve(); != nil {
return
}
if !.trackListener(&, true) {
return ErrServerClosed
}
defer .trackListener(&, false)
:= context.Background()
if .BaseContext != nil {
= .BaseContext()
if == nil {
panic("BaseContext returned a nil context")
}
}
var time.Duration // how long to sleep on accept failure
:= context.WithValue(, ServerContextKey, )
for {
, := .Accept()
if != nil {
select {
case <-.getDoneChan():
return ErrServerClosed
default:
}
if , := .(net.Error); && .Temporary() {
if == 0 {
= 5 * time.Millisecond
} else {
*= 2
}
if := 1 * time.Second; > {
=
}
.logf("http: Accept error: %v; retrying in %v", , )
time.Sleep()
continue
}
return
}
:=
if := .ConnContext; != nil {
= (, )
if == nil {
panic("ConnContext returned nil")
}
}
= 0
:= .newConn()
.setState(.rwc, StateNew, runHooks) // before Serve can return
go .serve()
}
}
if := .setupHTTP2_ServeTLS(); != nil {
return
}
:= cloneTLSConfig(.TLSConfig)
if !strSliceContains(.NextProtos, "http/1.1") {
.NextProtos = append(.NextProtos, "http/1.1")
}
:= len(.Certificates) > 0 || .GetCertificate != nil
if ! || != "" || != "" {
var error
.Certificates = make([]tls.Certificate, 1)
.Certificates[0], = tls.LoadX509KeyPair(, )
if != nil {
return
}
}
:= tls.NewListener(, )
return .Serve()
}
func ( *Server) ( *net.Listener, bool) bool {
.mu.Lock()
defer .mu.Unlock()
if .listeners == nil {
.listeners = make(map[*net.Listener]struct{})
}
if {
if .shuttingDown() {
return false
}
.listeners[] = struct{}{}
} else {
delete(.listeners, )
}
return true
}
func ( *Server) ( *conn, bool) {
.mu.Lock()
defer .mu.Unlock()
if .activeConn == nil {
.activeConn = make(map[*conn]struct{})
}
if {
.activeConn[] = struct{}{}
} else {
delete(.activeConn, )
}
}
func ( *Server) () time.Duration {
if .IdleTimeout != 0 {
return .IdleTimeout
}
return .ReadTimeout
}
func ( *Server) () time.Duration {
if .ReadHeaderTimeout != 0 {
return .ReadHeaderTimeout
}
return .ReadTimeout
}
func ( *Server) () bool {
return atomic.LoadInt32(&.disableKeepAlives) == 0 && !.shuttingDown()
}
func ( *Server) () bool {
return .inShutdown.isSet()
}
func ( *Server) ( bool) {
if {
atomic.StoreInt32(&.disableKeepAlives, 0)
return
}
atomic.StoreInt32(&.disableKeepAlives, 1)
func ( *Server) () error {
.nextProtoOnce.Do(.onceSetNextProtoDefaults)
return .nextProtoErr
}
func ( *Server) () error {
.nextProtoOnce.Do(.onceSetNextProtoDefaults_Serve)
return .nextProtoErr
}
func ( *Server) () {
if .shouldConfigureHTTP2ForServe() {
.onceSetNextProtoDefaults()
}
}
if .TLSNextProto == nil {
:= &http2Server{
NewWriteScheduler: func() http2WriteScheduler { return http2NewPriorityWriteScheduler(nil) },
}
.nextProtoErr = http2ConfigureServer(, )
}
}
testContext context.Context
}
func ( *timeoutHandler) () string {
if .body != "" {
return .body
}
return "<html><head><title>Timeout</title></head><body><h1>Timeout</h1></body></html>"
}
func ( *timeoutHandler) ( ResponseWriter, *Request) {
:= .testContext
if == nil {
var context.CancelFunc
, = context.WithTimeout(.Context(), .dt)
defer ()
}
= .WithContext()
:= make(chan struct{})
:= &timeoutWriter{
w: ,
h: make(Header),
req: ,
}
:= make(chan interface{}, 1)
go func() {
defer func() {
if := recover(); != nil {
<-
}
}()
.handler.ServeHTTP(, )
close()
}()
select {
case := <-:
panic()
case <-:
.mu.Lock()
defer .mu.Unlock()
:= .Header()
for , := range .h {
[] =
}
if !.wroteHeader {
.code = StatusOK
}
.WriteHeader(.code)
.Write(.wbuf.Bytes())
case <-.Done():
.mu.Lock()
defer .mu.Unlock()
.WriteHeader(StatusServiceUnavailable)
io.WriteString(, .errorBody())
.timedOut = true
}
}
type timeoutWriter struct {
w ResponseWriter
h Header
wbuf bytes.Buffer
req *Request
mu sync.Mutex
timedOut bool
wroteHeader bool
code int
}
var _ Pusher = (*timeoutWriter)(nil)
func ( *timeoutWriter) ( string, *PushOptions) error {
if , := .w.(Pusher); {
return .Push(, )
}
return ErrNotSupported
}
func ( *timeoutWriter) () Header { return .h }
func ( *timeoutWriter) ( []byte) (int, error) {
.mu.Lock()
defer .mu.Unlock()
if .timedOut {
return 0, ErrHandlerTimeout
}
if !.wroteHeader {
.writeHeaderLocked(StatusOK)
}
return .wbuf.Write()
}
func ( *timeoutWriter) ( int) {
checkWriteHeaderCode()
switch {
case .timedOut:
return
case .wroteHeader:
if .req != nil {
:= relevantCaller()
logf(.req, "http: superfluous response.WriteHeader call from %s (%s:%d)", .Function, path.Base(.File), .Line)
}
default:
.wroteHeader = true
.code =
}
}
func ( *timeoutWriter) ( int) {
.mu.Lock()
defer .mu.Unlock()
.writeHeaderLocked()
}
type globalOptionsHandler struct{}
func (globalOptionsHandler) ( ResponseWriter, *Request) {
.Header().Set("Content-Length", "0")
type initALPNRequest struct {
ctx context.Context
c *tls.Conn
h serverHandler
}
func ( initALPNRequest) () context.Context { return .ctx }
func ( initALPNRequest) ( ResponseWriter, *Request) {
if .TLS == nil {
.TLS = &tls.ConnectionState{}
*.TLS = .c.ConnectionState()
}
if .Body == nil {
.Body = NoBody
}
if .RemoteAddr == "" {
.RemoteAddr = .c.RemoteAddr().String()
}
.h.ServeHTTP(, )
}
type loggingConn struct {
name string
net.Conn
}
var (
uniqNameMu sync.Mutex
uniqNameNext = make(map[string]int)
)
func ( string, net.Conn) net.Conn {
uniqNameMu.Lock()
defer uniqNameMu.Unlock()
uniqNameNext[]++
return &loggingConn{
name: fmt.Sprintf("%s-%d", , uniqNameNext[]),
Conn: ,
}
}
func ( *loggingConn) ( []byte) ( int, error) {
log.Printf("%s.Write(%d) = ....", .name, len())
, = .Conn.Write()
log.Printf("%s.Write(%d) = %d, %v", .name, len(), , )
return
}
func ( *loggingConn) ( []byte) ( int, error) {
log.Printf("%s.Read(%d) = ....", .name, len())
, = .Conn.Read()
log.Printf("%s.Read(%d) = %d, %v", .name, len(), , )
return
}
func ( *loggingConn) () ( error) {
log.Printf("%s.Close() = ...", .name)
= .Conn.Close()
log.Printf("%s.Close() = %v", .name, )
return
}
type checkConnErrorWriter struct {
c *conn
}
func ( checkConnErrorWriter) ( []byte) ( int, error) {
, = .c.rwc.Write()
if != nil && .c.werr == nil {
.c.werr =
.c.cancelCtx()
}
return
}
func ( []byte) ( int) {
for , := range {
if == '\r' || == '\n' {
++
continue
}
break
}
return
}
func ( []string, string) bool {
for , := range {
if == {
return true
}
}
return false
}
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |