Table of Contents generated with DocToc

DetectContentType

1
	func DetectContentType(data []byte) string

DetectContentType函数的功能是根据data字节数组的内容来判定它的Content-Type值。该函数最多取data数据的前512个字节来进行判断,函数内部会先过滤掉一些空白字符,然后就根据一定的匹配算法来进行匹配,如果没有匹配到任何已知的类型,就会返回一个 “application/octet-stream”。

Error

1
	func Error(w ResponseWriter, error string, code int)

Error方法将会把用户指定的error和状态码写入ResponseWriter。但是调用这个函数的人需要确保没有进一步的信息写入到ResponseWriter。 实现代码是非常简单的,就是向response的header中写入数据:

1
2
3
4
5
6
	func Error(w ResponseWriter, error string, code int) {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.WriteHeader(code)
		fmt.Fprintln(w, error)
	}

Handle

1
	func Handle(pattern string, handler Handler)

Handle这个方法以及HandleFunc,ServeHttp,DefaultServeMux等都是golang实现的http框架中非常重要的元素,这篇文章接下来的篇幅我将和大家请一起学习它们,以了解一个http请求是如何通过golang实现的http框架来到服务后端处理并最终返回给客户端的。

简单来说,handle这个函数就是把请求的url和处理这个请求的方法绑定到一起。它的实现如下:

1
2
3
4
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

ServeMux

可以看出在Handle方法里面调用了DefaultServeMux的handle方法来实现pattern和handler的绑定操作。那么这个DefaultServeMux是什么呢?DefaultServeMux是ServeMux类型变量的一个指针,ServeMux类型的定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
//
// Patterns name fixed, rooted paths, like "/favicon.ico",
// or rooted subtrees, like "/images/" (note the trailing slash).
// Longer patterns take precedence over shorter ones, so that
// if there are handlers registered for both "/images/"
// and "/images/thumbnails/", the latter handler will be
// called for paths beginning "/images/thumbnails/" and the
// former will receive requests for any other paths in the
// "/images/" subtree.
//
// Note that since a pattern ending in a slash names a rooted subtree,
// the pattern "/" matches all paths not matched by other registered
// patterns, not just the URL with Path == "/".
//
// If a subtree has been registered and a request is received naming the
// subtree root without its trailing slash, ServeMux redirects that
// request to the subtree root (adding the trailing slash). This behavior can
// be overridden with a separate registration for the path without
// the trailing slash. For example, registering "/images/" causes ServeMux
// to redirect a request for "/images" to "/images/", unless "/images" has
// been registered separately.
//
// Patterns may optionally begin with a host name, restricting matches to
// URLs on that host only. Host-specific patterns take precedence over
// general patterns, so that a handler might register for the two patterns
// "/codesearch" and "codesearch.google.com/" without also taking over
// requests for "http://www.google.com/".
//
// ServeMux also takes care of sanitizing the URL request path,
// redirecting any request containing . or .. elements or repeated slashes
// to an equivalent, cleaner URL.
type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	hosts bool // whether any patterns contain hostnames
}

其实只看第一段落,我们对一个请求在golang的http框架中流动的过程就有了一个大致的了解: 服务端首先将一些pattern和handler绑定在一起,形成了一个pattern和handler一一对应的列表。ServeMux作为服务端一个http请求路由的角色出现,它将对进来的请求和我们已经构造好的pattern列表进行匹配,找到一个最佳的匹配项,然后调用相应的handler处理请求,最终将结果返回给用户。http包中的Handle方法之所以会调用DefaultServeMux的Handle方法,就是要把一条pattern和handler绑定的列表项注册到ServerMux上。以便ServerMux在接受到外部过来的请求之后可以依此来进行请求的派发。

文档中既然说到会找到一个最佳的匹配项,那么ServeMux中就会按照一定的匹配规则去做:

  1. 如果Pattern一个绝对路径指定的文件或者说是一个目录,那么此时,pattern长度越长与请求的匹配度越高。如一个请求[http://localhost:9090/abc/def/]()。假设我们注册了两个pattern和handler的表项。一个是/abc/另外一个是/abc/def/,这个时候ServeMux会优先匹配后者。并且需要注意的是,一个请求如果最后是以斜线结尾的,并且没有匹配到任何一个已经注册的Pattern-Handler表项,如果根目录/这个pattern已经被注册则会命中此条规则。
  2. 如果一个子目录的pattern已经注册好了,此时外部进来的一个请求其余的部分都命中这个pattern,但是唯独最后一个斜线不匹配,此时golang的http框架会将这个请求重定向到已经注册好的子目录的handler上。但是如果不带斜线的pattern已经被明确的注册过了,那么此时golang就不会再进行重定向操作。
  3. 如果hosts被置为TRUE,那么在进行匹配的时候就会对请求的path加上hostname作为pattern来进行匹配。

了解了ServeMux是一个类似http路由的组件,那么接下来就看看ServeMux的Handle方法都做了些什么。

ServeMux.Handle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
	
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern " + pattern)
	}
	if handler == nil {
		panic("http: nil handler")
	}
	if mux.m[pattern].explicit {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

	if pattern[0] != '/' {
		mux.hosts = true
	}

	// Helpful behavior:
	// If pattern is /tree/, insert an implicit permanent redirect for /tree.
	// It can be overridden by an explicit registration.
	n := len(pattern)
	if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
		// If pattern contains a host name, strip it and use remaining
		// path for redirect.
		path := pattern
		if pattern[0] != '/' {
			// In pattern, at least the last character is a '/', so
			// strings.Index can't be -1.
			path = pattern[strings.Index(pattern, "/"):]
		}
		url := &url.URL{Path: path}
		mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
	}
}

因为Handle方法会修改ServeMux中的Map,为了实现线程安全的Handle方法,一进来就对Handle这个函数加上了写锁。紧接着就是一些参数的检查工作,比如注册的pattern是否为空,handler是否为nil以及该pattern是否已经注册过一次了,这些都是属于非法的注册行为。如果以上的检查都通过了,那么golang将会在ServeMux中的Map内增加一条注册记录。如果此时我们的pattern是以斜线结尾的,那么golang会检测不以斜线结尾的pattern是否已经注册过,如果没有注册过,它会为我们将不带斜线的pattern注册,并且handler重定向到带有斜线的pattern对应的handler。这里需要注意一点:

  1. 通过重定向的方式来注册不带斜线的pattern的时候,explicit并没有置为true,这就说明如果我们以后要明确注册一个不以斜线结尾的pattern的时候,是可以实现覆盖的效果的,不会被前面的检查条件挡住。

回忆一下我们现在都了解了哪些知识点:首先可以通过一个Http包的Handle方法将一个pattern和handler注册起来,然后发现注册的这个操作实际上是由ServeMux这个类型的对象来实现的,它相当于一个Http的路由,ServeMux在注册时候,会做出一些改变,如为不带斜线的pattern默认添加重定向的Handler等。那么处理一个和Pattern匹配的请求的Handler是个什么东西呢?

Handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// A Handler responds to an HTTP request.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished; it
// is not valid to use the ResponseWriter or read from the
// Request.Body after or concurrently with the completion of the
// ServeHTTP call.
//
// Depending on the HTTP client software, HTTP protocol version, and
// any intermediaries between the client and the Go server, it may not
// be possible to read from the Request.Body after writing to the
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
// Except for reading the body, handlers should not modify the
// provided Request.
//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
// and hangs up the connection. To abort a handler so the client sees
// an interrupted response but the server doesn't log an error, panic
// with the value ErrAbortHandler.
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

Handler是一个接口,它里面包含了一个ServeHttp的方法,也就是说,任何一个实现了ServeHttp方法的对象,都可以被赋值给Handler对象。Handler正如我们前面所讲到的那样,它负责处理一个http的请求。实际的处理逻辑应该在ServeHttp方法中实现。这个方法接收两个参数,一个是ResponseWriter,一个是Request。ServeHttp方法要保证先读取request,处理完之后把返回的header和body部分写入到responsewriter中,这个处理的顺序不能够颠倒。并且在ServeHttp在将响应结果写入到responseWriter之后,就不应该在使用ResponseWriter和request这两个参数了。说白了,当ServeHttp写入结果到respinseWriter的那一刻,整个http请求的处理就结束了。并且,golang明确的提示我们,ServeHttp内部不应该去修改request的相关信息。当ServeHttp出现panic的时候,golang会recover住它,在服务的日志中输出堆栈信息且挂起这个链接。如果直接终止一个handler,那么服务不会输出错误,而是直接panic崩掉,同时客户端也会得到一个中断的响应。

ServeMux.ServeHTTP

既然现在,pattern有了,handler有了,ServeMux的注册功能也有了,那么ServeMux作为一个路由是如何将一个http请求匹配到已经注册的pattern,并且交由这个pattern对应的handler处理的呢?这也就是ServeMux派发request的一个过程。ServeMux也实现了一个ServeHttp的方法,这证明它其实也可以算作是一个handler,那么在外部请求进入到服务的时候,ServeMux会作为一个前置的handler来处理这个请求,而ServeMux的处理方式就是去匹配pattern并且派发这个请求给具体处理逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}

可以看到ServeMux的ServeHttp方法在处理请求的时候,会先检查request中的url是否为*。然后他会检查请求的http协议版本至少要在1.1及以上才是合法的,否则就返回给客户端一个错误。然后,serveMux将会调用Handler方法,该方法中将会检查request中的信息并匹配到相应的pattern,返回给我们一个能够处理该请求的一个handler。最后调用该handler实现的ServeHttp方法处理这个请求。看起来ServeHttp这个方法的重点就在于这个Handler方法的实现了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Handler returns the handler to use for the given request,
// consulting r.Method, r.Host, and r.URL.Path. It always returns
// a non-nil handler. If the path is not in its canonical form, the
// handler will be an internally-generated handler that redirects
// to the canonical path.
//
// Handler also returns the registered pattern that matches the
// request or, in the case of internally-generated redirects,
// the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request,
// Handler returns a ``page not found'' handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
	if r.Method != "CONNECT" {
		if p := cleanPath(r.URL.Path); p != r.URL.Path {
			_, pattern = mux.handler(r.Host, p)
			url := *r.URL
			url.Path = p
			return RedirectHandler(url.String(), StatusMovedPermanently), pattern
		}
	}

	return mux.handler(r.Host, r.URL.Path)
}

Handler这个方法是永远不会返回一个值为nil的handler,即使最后request没有匹配到任何的handler,函数也会返回一个“page not found”的handler。匹配之前,handler会先调用cleanPath来对请求的urlpath做一些清理工作,排除匹配时候的干扰。清理之后的path如果和request内的urlpath不一致的话,那就说明,request内的urlpath是不符合规范的,不是一个标准的形式,此时,golang将会返回一个用于重定向这个请求的Handler。如果确实发生了这种情况,那么ServeMux的ServeHttp方法将会调用RedirectHandler类型的ServeHttp方法来处理请求。通过观察RedirectHandler的ServeHttp方法的实现,我们知道,RedirectHandler在处理请求的时候最终是将重定向的状态码和location头部字段写入到Response的header中,其中location的内容就是根据我们规范过的path来确定的。

如果request传递进来的path就是一个标准的形式,那么将会调用mux.handler方法开始进行匹配工作.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
	mux.mu.RLock()
	defer mux.mu.RUnlock()

	// Host-specific pattern takes precedence over generic ones
	if mux.hosts {
		h, pattern = mux.match(host + path)
	}
	if h == nil {
		h, pattern = mux.match(path)
	}
	if h == nil {
		h, pattern = NotFoundHandler(), ""
	}
	return
}

handler一开始就用读锁锁住了整个函数,说明在这个函数中会读取ServeMux中的handler注册的map。之前在了解ServeMux成员结构的时候说过,如果ServeMux开启了强制带域名匹配,那么它是第一优先级的,待匹配的path也要加上host。如果强制带域名匹配没有匹配到,那么就会进行其他正常的匹配,否则认为没有匹配到,返回有一个NotFoundHanlder。NotFoundHanlder在处理请求的时候也是向response写入相应的错误信息,返回给客户端。ServeMux的math方法将会遍历注册了pattern和handler的map,对pattern和path进行匹配。在匹配的时候如果发现注册map当中的pattern与path有多个匹配情况,那么将会选取pattern最长的那个作为最佳的匹配项,这一点在介绍ServeMux类型的时候也是说过的。

其实讲到这里,一个请求从外部进来,到ServeMux,到handler,到ServeHttp,最终处理完成返回给客户端的一条线就已经是通的了。其中涉及到了一些细枝末节的东西,如path.Clean是如何清洗urlpath的,这些我们暂时都不做过多的了解。我们的重点要放在golang实现的http框架上面,了解它是如何进行运作的。

之前我们是从http包中的Handle说起的,那么紧接着我们就来看看HandleFunc这个函数都做了什么以及与Handle方法有何异同。

HandleFunc

1
2
3
4
5
6
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

HandleFunc这个函数看起来和Handle没有什么区别,都是调用了DefaultServeMux的同名方法对pattern和处理与这个pattern匹配的请求的handler进行注册。只不过HandleFunc注册的handler并不是一个Handler类型的对象,而直接是一个函数,这个函数的原型和Handler类型中的ServeHttp方法是一样的。

1
2
3
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handle(pattern, HandlerFunc(handler))
}

ServeMux的HandleFunc方法内部调用的还是Handle方法,只不过对我们传递进来的函数方法通过HandlerFunc转换成一个Handler的对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

在golang中我们知道,函数也是一种值,这种值的类型我暂且就把它称作是一个函数类型吧。既然是类型,那么在golang中就可以实现这个类型相应的成员方法,HandlerFunc就是这样。ServeMux的Handle方法第二个参数就是接受了一个Handler对象,只要实现了ServeHttp方法,都可以赋值给它。HandlerFunc也实现了一个,只不过在内部调用了自己。感觉是golang利用Interface的特性玩了一个小把戏,为我们提供了额外的一种方式来对pattern和handler进行注册。

ListenAndServe

listenAndServe这个方法应该是处理一个http请求的流程中最重要的一个方法了。从函数名上我们就可以看出,这个函数起到了监听和提供服务的作用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ListenAndServe listens on the TCP network address addr
// and then calls Serve with handler to handle requests
// on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// Handler is typically nil, in which case the DefaultServeMux is
// used.
//
// A trivial example server is:
//
//	package main
//
//	import (
//		"io"
//		"net/http"
//		"log"
//	)
//
//	// hello world, the web server
//	func HelloServer(w http.ResponseWriter, req *http.Request) {
//		io.WriteString(w, "hello, world!\n")
//	}
//
//	func main() {
//		http.HandleFunc("/hello", HelloServer)
//		log.Fatal(http.ListenAndServe(":12345", nil))
//	}
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

ListenAndServe通过监听一个tcp的网络地址来为通过某个tcp链接发来的请求提供服务。可以看到该函数的第二个参数就是一个Handler对象,ListenAndServe显然已经是golang服务框架中最外层的入口了,那么首先处理从外部进来请求的handler自然应该是ServeMux。当我们指定第二个参数为nil的时候,golang就会使用DefaultServeMux作为我们整个服务的路由handler。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// If srv.Addr is blank, ":http" is used.
// ListenAndServe always returns a non-nil error.
func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

http包中的ListenAndServe方法在其内部通过定义了一个server对象,调用server的ListenAndServe方法。这个Server看起来就是整个golang的http服务框架的核心类型。它的ListenAndServe方法监听了本机的addr端口并调用Serve方法来开启整个服务。前面我们讲到了一个请求到了ServeMux之后是如何被派发并且处理的,那么接下来,我们就来研究一下一个请求是如何从客户端到达ServeMux的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
	
// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// For HTTP/2 support, srv.TLSConfig should be initialized to the
// provided listener's TLS Config before calling Serve. If
// srv.TLSConfig is non-nil and doesn't include the string "h2" in
// Config.NextProtos, HTTP/2 support is not enabled.
//
// Serve always returns a non-nil error. After Shutdown or Close, the
// returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	if fn := testHookServerServe; fn != nil {
		fn(srv, l)
	}
	var tempDelay time.Duration // how long to sleep on accept failure

	if err := srv.setupHTTP2_Serve(); err != nil {
		return err
	}

	srv.trackListener(l, true)
	defer srv.trackListener(l, false)

	baseCtx := context.Background() // base is always background, per Issue 16220
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
	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)
	}
}

server对象调用了Serve方法之后,可以看到,在其实现中有一个死循环,不断的接受从外部而来的网络连接,为每一个网络连接都开启一个goroutine调用Conn类型对象的serve方法来处理这条连接上发来的http请求。Serve函数大体的逻辑就是这样,一些实现的细节我们暂时先不追究,但是有一点需要注意的是,server调用了newConn创建了一个Conn类型的对象,这个对象内其中一个成员就是我们的server对象,也就是说,再创建这个Conn对象的时候把server自己也传递了进去。

了解了以上我们说的几点就可以继续向下研究了。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
func (c *conn) serve(ctx context.Context) {
	c.remoteAddr = c.rwc.RemoteAddr().String()
	defer func() {
		if err := recover(); err != nil && err != ErrAbortHandler {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
		}
		if !c.hijacked() {
			c.close()
			c.setState(c.rwc, StateClosed)
		}
	}()

	if tlsConn, ok := c.rwc.(*tls.Conn); ok {
		if d := c.server.ReadTimeout; d != 0 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
		}
		if d := c.server.WriteTimeout; d != 0 {
			c.rwc.SetWriteDeadline(time.Now().Add(d))
		}
		if err := tlsConn.Handshake(); err != nil {
			c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
			return
		}
		c.tlsState = new(tls.ConnectionState)
		*c.tlsState = tlsConn.ConnectionState()
		if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {
				h := initNPNRequest{tlsConn, serverHandler{c.server}}
				fn(c.server, tlsConn, h)
			}
			return
		}
	}

	// 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)
		if c.r.remain != c.server.initialReadLimitSize() {
			// If we read any bytes off the wire, we're active.
			c.setState(c.rwc, StateActive)
		}
		if err != nil {
			const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"

			if err == errTooLarge {
				// Their HTTP client may or may not be
				// able to read this if we're
				// responding to them and hanging up
				// while they're still writing their
				// request. Undefined behavior.
				const publicErr = "431 Request Header Fields Too Large"
				fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
				c.closeWriteAndWait()
				return
			}
			if isCommonNetReadError(err) {
				return // don't reply
			}

			publicErr := "400 Bad Request"
			if v, ok := err.(badRequestError); ok {
				publicErr = publicErr + ": " + string(v)
			}

			fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
			return
		}

		// Expect 100 Continue support
		req := w.req
		if req.expectsContinue() {
			if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
				// Wrap the Body reader with one that replies on the connection
				req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
			}
		} else if req.Header.get("Expect") != "" {
			w.sendExpectationFailed()
			return
		}

		c.curReq.Store(w)

		if requestBodyRemains(req.Body) {
			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
		} else {
			if w.conn.bufr.Buffered() > 0 {
				w.conn.r.closeNotifyFromPipelinedRequest()
			}
			w.conn.r.startBackgroundRead()
		}

		// HTTP cannot have multiple simultaneous active requests.[*]
		// Until the server replies to this request, it can't read another,
		// so we might as well run the handler in this goroutine.
		// [*] Not strictly true: HTTP pipelining. We could let them all process
		// in parallel even if their responses need to be serialized.
		// But we're not going to implement HTTP pipelining because it
		// was never deployed in the wild and the answer is HTTP/2.
		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{})
	}
}

在conn对象的serve方法中,最引人注目的代码不过有两行,而这两行也是最核心的:

  1. serve方法中开启了一个死循环,死循环中不断的调用readRequest(ctx)来处理从这条连接上发送过来的http请求。
  2. serverHandler{c.server}.ServeHTTP(w, w.req),其次便是这一行代码。

我们之前说过,conn对象在创建的时候是把server对象也包含进来的,而server对象中有一个Handler成员,它在我们定义这个server对象的时候被赋值为了nil,如果我们要使用server对象中的这个Handler成员的话,就会默认使用golang为我们预定义的DefaultServerMux对象,也就是我们所说的服务的http路由。在conn对象的serve方法内,通过readRequest来读取请求中的内容,然后调用DefaultServerMux对象的ServeHttp方法进行请求的派发处理。至此,一个http请求就是通过这样的方式从服务的外部传输到服务的内部,最后调用了DefaultServerMux的ServeHttp方法开始处理它。并且在处理完这个request之后我们还会检查是否server开启了keepalives的特性,如果开启了,那么就会保持这个闲置的链接一段时间直到请求到达在进行处理,如果没有开启这个特性那么此链接最终就会被关闭。