上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
handler
处理器方法的两个参数,分别对应返回和请求数据。
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
Request
:客户端发送的请求信息。ResponseWriter
:返回给客户端的数据。
- 处理器不应修改请求数据。
- 处理器结束后,会进行资源清理,不应再持有或访问这两个参数。
- 返回调用次序:
Header.Set
、WriteHeader
、Write
。 - 善用
Error
、Redirect
、SetCookie
、ParseTime
函数。
type TestHandler struct{} func (*TestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // request fmt.Println("method:", req.Method) fmt.Println(" url:", req.URL) fmt.Println(" ", req.URL.Query()) fmt.Println("header:") for k, v := range req.Header { fmt.Println(" ", k, v) } fmt.Println(" body:") data, _ := io.ReadAll(req.Body) fmt.Println(" ", string(data)) // response w.Header().Set("test", "demo server") w.WriteHeader(http.StatusCreated) io.WriteString(w, "hello, world!") } func main() { log.Fatalln(http.ListenAndServe(":8080", new(TestHandler))) }
参数
对连接和请求数据进行处理,包装成 Request
、 response
对象。
请求结束时关闭和清理资源,所以不该继续持有或访问。
// http/server.go func (c *conn) readRequest(ctx context.Context) (w *response, err error) { req, err := readRequest(c.bufr) w = &response{ req: req, reqBody: req.Body, } return w, nil }
// http/server.go func (w *response) finishRequest() { w.cw.close() w.reqBody.Close() }
在 response.Write
内检查并调用 WriteHeader
,而后者不允许重复设定。
所以,要返回非 200 状态,须在 Write
之前调用。
// http/server.go func (w *response) write(int, []byte, string) (n int, err error) { if !w.wroteHeader { w.WriteHeader(StatusOK) } } func (w *response) WriteHeader(code int) { if w.wroteHeader { w.conn.server.logf("http: superfluous ...", ...) return } w.wroteHeader = true w.status = code }
路由
自带的 ServeMux
路由只能算作一个范例,功能很弱,正式项目中很少使用。
其作用是统一接收请求,然后按路径模式分发给其他已注册处理器。
func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, "hello, world!") }) srv := &http.Server{ Addr: ":8080", Handler: mux, ReadTimeout: time.Second * 10, WriteTimeout: time.Second * 10, } log.Fatalln(srv.ListenAndServe()) }
Handle
:注册处理器。HandleFunc
:将函数包装成处理器,并注册。Handler
:根据请求(request)返回对应的注册处理器。
// http/server.go type ServeMux struct { m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. }
// http/server.go func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() // 不能重复注册。 if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } // 创建注册字典。 if mux.m == nil { mux.m = make(map[string]muxEntry) } // 存入字典。 e := muxEntry{h: handler, pattern: pattern} mux.m[pattern] = e // 对 / 结尾的维持一个有序排列。 if pattern[len(pattern)-1] == '/' { mux.es = appendSorted(mux.es, e) } }
算法实现太过简单,长路径模式优先。
如果没有任何匹配模式,则以 NotFoundHandler
处理。
// http/server.go func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { // 找出与请求匹配的处理器。 h, _ := mux.Handler(r) h.ServeHTTP(w, r) }
// http/server.go func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { return mux.handler(host, r.URL.Path) } func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return } func (mux *ServeMux) match(path string) (h Handler, pattern string) { // 精确匹配。 v, ok := mux.m[path] if ok { return v.h, v.pattern } // 模糊匹配,前缀搜索。(从长到短排列) for _, e := range mux.es { if strings.HasPrefix(path, e.pattern) { return e.h, e.pattern } } return nil, "" }
未设置 Server.Handler
,则默认使用 DefaultServeMux
处理。
// http/server.go var DefaultServeMux = &defaultServeMux var defaultServeMux ServeMux func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
处理器
标准库实现的常用处理器:
NotFound, NotFoundHandler
:没找到。(404)Redirect, RedirectHandler
:重定向。(301, 302, 303)TimeoutHandler
:包装其他处理器,设置超时。StripPrefix
:包装其他处理器,移除请求路径前缀。FileServer
:静态资源。(文件)httputil/ReverseProxy
:反向代理。
利用 HandlerFunc
将函数转型即可实现处理器,简化编码。
// http/server.go type HandlerFunc func(ResponseWriter, *Request) // 不是 HandleFunc !!! func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
还可结合闭包绑定状态,例如以 channel
实现 semaphore
,控制最大并发连接。
func connLimiter(h http.Handler, max int) http.Handler { sema := make(chan struct{}, max) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sema <- struct{}{} defer func() { <-sema }() h.ServeHTTP(w, r) }) }
测试
借助 httptest
可非常方便测试处理器。
package main import ( "fmt" "io" "net/http" "net/http/httptest" ) func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, "hello, world!") } func main() { // 直接创建 Request, Response 对 Handler 进行测试。 req := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil) w := httptest.NewRecorder() hello(w, req) resp := w.Result() b, _ := io.ReadAll(resp.Body) fmt.Println(string(b)) }
package main import ( "fmt" "io" "net/http" "net/http/httptest" ) func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, "hello, world!") } func main() { // 使用随机端口启用 h2 服务器和客户端通信。 // 可完成两端结合测试。 s := httptest.NewUnstartedServer(http.HandlerFunc(hello)) s.EnableHTTP2 = true s.StartTLS() defer s.Close() resp, _ := s.Client().Get(s.URL) b, _ := io.ReadAll(resp.Body) resp.Body.Close() fmt.Println(resp) fmt.Println(string(b)) }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论