http server处理请求和返回repsonse的简单流程
if BConfig.Listen.EnableHTTP {
go func() {
app.Server.Addr = addr
logs.Info("http server Running on http://%s", app.Server.Addr)
if BConfig.Listen.ListenTCP4 {
ln, err := net.Listen("tcp4", app.Server.Addr)
if err != nil {
logs.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
if err = app.Server.Serve(ln); err != nil {
logs.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
if err := app.Server.ListenAndServe(); err != nil {
logs.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}
上方代码块第六行代码根据network类型和服务IP,返回一个监听,用来监听请求这个IP的request。到初始化监听结构体的函数里可以看到,如果是tcp请求,下方代码块第15行则会回传一个tcp的listener。
func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) {
addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
if err != nil {
return nil, &OpError{
Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
}
sl := &sysListener{
ListenConfig: *lc,
network: network,
address: address,
}
var l Listener
la := addrs.first(isIPv4)
switch la := la.(type) {
case *TCPAddr:
l, err = sl.listenTCP(ctx, la)
case *UnixAddr:
l, err = sl.listenUnix(ctx, la)
default:
return nil, &OpError{
Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{
Err: "unexpected address type", Addr: address}}
}
if err != nil {
return nil, &OpError{
Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // l is non-nil interface containing nil pointer
}
return l, nil
}
当有请求过来之后,生成的Listener就会尝试建立连线,进而处理连线的请求,第一个代码块的第13行就是在做这件事,代码如下。参数l就是上方代码回传的Listener,下方第13行,会先追踪穿进来的Listener是否正常的,如果是关闭的,会回传服务器关闭的错误(第14行)。
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
l = &onceCloseListener{
Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
var tempDelay time.Duration // how long to sleep on accept failure
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
之后,才是重头戏,上方第21行-第48行。这段代码通过一个for循环,一直在监听是否有请求,如果有监听到请求则尝试建立connection,见上方第22行。详细代码见下方代码块。上方的l.Accept是一个接口方法,因为传进来的是TcpListener,而TcpListener又实现了/net/net.go里面的Listener接口。所以上方的l.Accept调的就是下方的Accept函数,返回一个类型为net.Conn的连接。如果Accept没有报错,上方第45行,则会根据这个返回的net.Conn新初始化一个/net/http/server.go的Conn结构体。这个/net/http/server.go的Conn结构体代表的是HTTP连接的服务器端。因为连线是很多的,而服务器要一直监听,所以开协程处理新建立的请求,上方第47行,详见下下方代码块。
func (l *TCPListener) Accept() (Conn, error) {
if !l.ok() {
return nil, syscall.EINVAL
}
c, err := l.accept()
if err != nil {
return nil, &OpError{
Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
}
return c, nil
}
下方代码块:
第7行-第9行是对有加密的请求做处理。
第18行-第19行初始化Conn的buffer read和buffer write,定义了一个读的缓存流和一个写的缓存流。
第21行,开一个for接受这个connect的请求,开for循环的原因是因为该connection可能会被复用,并且是一个长连接。
第22行,获取请求的request内容,代码详见下下方代码块。
第28行-第32行,读取body剩下内容。
第34行,ServeHTTP封装了http server实现的ServeHttp接口,在读取完request body后,匹配URI请求的方法并执行(详见Beego_code_Router中的ServeHttp方法解析)。
最后,判断该请求是否是一个长连接,如果是长连接则设置空闲超时时间及读请求的最后时间,否则结束该次请求。一个for的结束就代表着这个request处理完毕。
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
...
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
...
}
// HTTP/1.x from here on.
ctx, cancelCtx := context.WithCancel(ctx)
c.cancelCtx = cancelCtx
defer cancelCtx()
c.r = &connReader{
conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{
c}, 4<<10)
for {
w, err := c.readRequest(ctx)
...
c.curReq.Store(w)
if requestBodyRemains(req.Body) {
registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
} else {
w.conn.r.startBackgroundRead()
}
serverHandler{
c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
c.closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle)
c.curReq.Store((*response)(nil))
if !w.conn.server.doKeepAlives() {
// We're in shutdown mode. We might've replied
// to the user without "Connection: close" and
// they might think they can send another
// request, but such is life with HTTP/1.1.
return
}
if d := c.server.idleTimeout(); d != 0 {
c.rwc.SetReadDeadline(time.Now().Add(d))
if _, err := c.bufr.Peek(4); err != nil {
return
}
}
c.rwc.SetReadDeadline(time.Time{
})
}
}
下方代码块为/net/http/server.go conn结构体的readRequest方法,主要用于读取connection的请求,初始化response结构体。真正读取request的内容的方法在下方代码块的第30行。
第10行-第22行在设定读写超时时间。
第30行,读取request body的源码见下下方代码块。
func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
if c.hijacked() {
return nil, ErrHijacked
}
var (
wholeReqDeadline time.Time // or zero if none
hdrDeadline time.Time // or zero if none
)
t0 := time.Now()
if d := c.server.readHeaderTimeout(); d != 0 {
hdrDeadline = t0.Add(d)
}
if d := c.server.ReadTimeout; d != 0 {
wholeReqDeadline = t0.Add(d)
}
c.rwc.SetReadDeadline(hdrDeadline)
if d := c.server.WriteTimeout; d != 0 {
defer func() {
c.rwc.SetWriteDeadline(time.Now().Add(d))
}()
}
c.r.setReadLimit(c.server.initialReadLimitSize())
if c.lastMethod == "POST" {
// RFC 7230 section 3 tolerance for old buggy clients.
peek, _ := c.bufr.Peek(4) // ReadRequest will get err below
c.bufr.Discard(numLeadingCRorLF(peek))
}
req, err := readRequest(c.bufr, keepHostHeader)
if err != nil {
if c.r.hitReadLimit() {
return nil, errTooLarge
}
return nil, err
}
if !http1ServerSupportsRequest(req) {
return nil, badRequestError("unsupported protocol version")
}
c.lastMethod = req.Method
c.r.setInfiniteReadLimit()
...
ctx, cancelCtx := context.WithCancel(ctx)
req.ctx = ctx
req.RemoteAddr = c.remoteAddr
req.TLS = c.tlsState
if body, ok := req.Body.(*body); ok {
body.doEarlyClose = true
}
// Adjust the read deadline if necessary.
if !hdrDeadline.Equal(wholeReqDeadline) {
c.rwc.SetReadDeadline(wholeReqDeadline)
}
w = &response{
conn: c,
cancelCtx: cancelCtx,
req: req,
reqBody: req.Body,
handlerHeader: make(Header),
contentLength: -1,
closeNotifyCh: make(chan bool, 1),
// We populate these ahead of time so we're not
// reading from req.Header after their Handler starts
// and maybe mutates it (Issue 14940)
wants10KeepAlive: req.wantsHttp10KeepAlive(),
wantsClose: req.wantsClose(),
}
if isH2Upgrade {
w.closeAfterReply = true
}
w.cw.res = w
w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
return w, nil
}
下方代码块为/net/http/request.go中的readRequest方法,解析request请求,并返回Request对象。
该请求的request是http请求,所以在第7行读第一行的时候,是根据http的header的第一行读取,可见下图:
将读到的第一行数据拿去做解析,就可以得到method,requestURI,protocol。
第22行判断http的method是否为有效的method(OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT)。
第29行-第33行读取request header剩下的字段及字段值,以(key,value) pair的形式。
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error) {
tp := newTextprotoReader(b)
req = new(Request)
// First line: GET /index.html HTTP/1.0
var s string
if s, err = tp.ReadLine(); err != nil {
return nil, err
}
defer func() {
putTextprotoReader(tp)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
}()
var ok bool
req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s)
if !ok {
return nil, &badStringError{
"malformed HTTP request", s}
}
if !validMethod(req.Method) {
return nil, &badStringError{
"invalid method", req.Method}
}
...
// Subsequent lines: Key: value.
mimeHeader, err := tp.ReadMIMEHeader()
if err != nil {
return nil, err
}
req.Header = Header(mimeHeader)
...
return req, nil
}
&badStringError{
"malformed HTTP request", s}
}
if !validMethod(req.Method) {
return nil, &badStringError{
"invalid method", req.Method}
}
...
// Subsequent lines: Key: value.
mimeHeader, err := tp.ReadMIMEHeader()
if err != nil {
return nil, err
}
req.Header = Header(mimeHeader)
...
return req, nil
}