LoginSignup
8
6

More than 3 years have passed since last update.

【初学者向け】golangのListenAndServeについてまとめてみた。

Posted at

Webサーバを構築してくれるListenAndServeについてまとめてみました。

main.go
func main() {
    http.ListenAndServe(":8080", nil)
}
server.go
// 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.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

ListenAndServeは、ポート番号(string型)とハンドラ(Handler型)の引数を取ります。
このハンドラの引数にnilが渡された場合、デフォルトでDefaultServeMuxというServeMux型のハンドラが使用されます。
ソースのコメントにもあるように、基本的にnilを渡すのが正解のようなので、深追いしません。(初学者なので)

この二つの引数をもとに、Webサーバが起動し、リクエスト受付状態になります。

server.go
// 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. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
        return ErrServerClosed
    }
    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)})
}

レシーバが使われてますね。server型のポインタならListenAndServeが使えるようです。

server.go
// 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.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
...
    for {
        rw, e := l.Accept()
        if e != nil {
            ...
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

Serveメソッドの中で無限ループの最後で、goroutine上でserveメソッドが使用されています。

server.go
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
    serverHandler{c.server}.ServeHTTP(w, w.req)
...
}

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)
}

ServeHTTPメソッドの中まで追ってようやくhandlerにDefaultServerMuxをセットしていることが分かりました。
handlerの役割は、ResponseWriterにリクエストを書き込むことなので、ゴールが近そうです。

server.go
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)
}

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

ServeMux(マルチプレクサ?)のHandlerメソッドの中で、リクエストのURLパスにマッチするHandlerFuncが返されます。
最終的に返されたHandlerFuncに対して、ResponseWriterとリクエストを投げて、処理を行っています。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6