Go の http パッケージを使って HTTP Server を作るに当たっていろいろな書き方があるけど、例えば以下のようなコードを書いたとき、登場するの関数が何をしているのかあんまりわかっていなかったので調べた。
package main import ( "fmt" "net/http" ) type apiHandler struct{} func (apiHandler) ServeHTTP(http.ResponseWriter, *http.Request) {} func main() { mux := http.NewServeMux() mux.Handle("/api/", apiHandler{}) mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { // The "/" pattern matches everything, so we need to check // that we're at the root here. if req.URL.Path != "/" { http.NotFound(w, req) return } fmt.Fprintf(w, "Welcome to the home page!") }) }
環境
% go version go version go1.14.2 darwin/amd64
http.Handle()
URLのパターンをServeMuxに登録するための関数。
ServeMux 構造体に Handle メソッドが定義されているので、mux.Handle("パス(ex /hello)", ServeHTTP() を実装した構造体 (ex apiHandler{}))
みたいな形式でこれが呼び出される。
func (mux *ServeMux) Handle(pattern string, handler Handler) {}
の第二引数で Handler が定義されている。
// 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") } if handler == nil { panic("http: nil handler") } 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) } if pattern[0] != '/' { mux.hosts = true } } : // 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) }
http.HandleFunc()
特定のパターンを処理するためのハンドラ関数を DefaultServeMux
の変数に登録する関数。
HandleFunc()
関数内で、 DefaultServeMux
(http.ServeMux
型)のHandleFunc
メソッドを呼び出している。
// 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) }
http.ServeMux.HandleFunc
メソッドでは、ServeMux
がある場合は、第2引数の handler
を HandlerFunc
に変換して、自身の Handle() (DefaultServeMux.Handle(pattern, handler))
を呼び出し、 DefaultServeMux
に追加している。
ServeMux
がない場合は、DefaultServeMux.HandleFunc()
を呼び出して URL が登録される模様。
// HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler)) }
http.Handler
ServeHTTP
メソッドだけを持つ Interface。
ServeHTTP(ResponseWriter, *Request) を実装していれば Handler を満たす。
この ServeHTTP
メソッドはHTTPリクエストを受けてHTTPレスポンスを返す役割を持っている。
// 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 either closes the network connection or sends an HTTP/2 // RST_STREAM, depending on the HTTP protocol. 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) }
http.HandlerFunc
HandlerFunc()
は func(ResponseWriter, *Request)
関数の別名として定義されているもの。
f(w, r)
の形で ServeHTTP(w ResponseWriter, r *Request)
が呼び出される。
// 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) }
DefaultServeMux/ServeMux
ルーティングの処理を行う部分。
DefaultServeMux
は *ServeMux
型の変数で、 defaultServeMux
のポインタが DefaultServeMux
へ格納されている。
ServeMux
は、受け付けた HTTP リクエストの URL とそれに対応するハンドラを登録するために利用される構造体。
// 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 and the Host // header, stripping the port number and redirecting any request containing . or // .. elements or repeated slashes to an equivalent, cleaner URL. type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames } type muxEntry struct { h Handler pattern string } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = &defaultServeMux var defaultServeMux ServeMux
// A Server defines parameters for running an HTTP server. // The zero value for Server is a valid configuration. type Server struct { // Addr optionally specifies the TCP address for the server to listen on, // in the form "host:port". If empty, ":http" (port 80) is used. // The service names are defined in RFC 6335 and assigned by IANA. // See net.Dial for details of the address format. Addr string Handler Handler // handler to invoke, http.DefaultServeMux if nil
DefaultServeMux
を使用したルーティング処理は、ServeHTTP
メソッドで行われる。
メソッドに応じて分かれているように思われたが、http.Handler
でリクエストを取得して、ServeHTTP
メソッドが呼び出されているように思われる。
// 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) }
// serverHandler delegates to either the server's Handler or // DefaultServeMux and also handles "OPTIONS *" requests. type serverHandler struct { srv *Server } func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req) }
http.ServeMux.Handler
は下記で、今度は handler
メソッドが呼ばれている。
// 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. If the host contains a port, it is ignored // when matching handlers. // // The path and host are used unchanged for CONNECT requests. // // 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) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { // If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not. if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, // redirect for /tree/. if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } if path != r.URL.Path { _, pattern = mux.handler(host, path) url := *r.URL url.Path = path return RedirectHandler(url.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path) }
http.ServeMux.match
メソッドでハンドラを取得している模様。
// 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 }
// Find a handler on a handler map given a path string. // Most-specific (longest) pattern wins. func (mux *ServeMux) match(path string) (h Handler, pattern string) { // Check for exact match first. v, ok := mux.m[path] if ok { return v.h, v.pattern } // Check for longest valid match. mux.es contains all patterns // that end in / sorted from longest to shortest. for _, e := range mux.es { if strings.HasPrefix(path, e.pattern) { return e.h, e.pattern } } return nil, "" }
NewServeMux
NewServeMux()
は ServeMux
構造体を初期化するコンストラクタの役割となっているもの。
// 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 and the Host // header, stripping the port number and redirecting any request containing . or // .. elements or repeated slashes to an equivalent, cleaner URL. type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames } : : type muxEntry struct { explicit bool h Handler pattern string } : : // NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return new(ServeMux) } : : var DefaultServeMux = NewServeMux()
まとめ
とりあえず全体的に http.Handler の ServeHTTP(ResponseWriter, *Request) で処理されることになるっぽい。
Handle などの関数は http パッケージの関数であり、ServeMux のメソッドでもある模様。実際には DefaultServeMux が持っている関数を呼び出すためのもので、例えば、http.Handle を呼び出すコードは、DefaultServeMux のメソッド Handle を呼び出すことになるようだった。
参考
- go - Git at Google
- http - The Go Programming Language
- Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズ
- GoのHTTPサーバーの実装 - 技術メモ
- go - How does ServeHTTP work? - Stack Overflow
- Goのhttp.Handlerやhttp.HandlerFuncをちゃんと理解する - oinume journal
- インタフェースの実装パターン #golang - Qiita
- 【Go】net/httpパッケージを読んでhttp.HandleFuncが実行される仕組み - Qiita
- なぜServeHTTPを実装しているだけで呼ばれるのかがわからない|teratail