🌶️

golang net/httpパッケージのソースコードを読んでみます

2022/11/24に公開

サンプルコード

package main

import (
	"fmt
	"net/http"
)

type Handler struct {
}

func NewHandler() *Handler {
	return &Handler{}
}

func (h *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	fmt.Println(req.Header)

	writer.WriteHeader(200)
}

func main() {
	server := &http.Server{
		Handler: NewHandler(),
		Addr:    "localhost:8080",
	}
	server.ListenAndServe()
}

起動流れ

  1. main関数の以下の行を実行します
server.ListenAndServe()
  1. net/httpパッケージのListenAndServeを実行します

https://github.com/golang/go/blob/master/src/net/http/server.go#L2976-L2989

中にはtcp通信のListenerリソースを用意します

https://github.com/golang/go/blob/72fdecafc0c1863f7540bd827a387e7e56836187/src/net/http/server.go#L2984

  1. tcp通信のListenerリソースが検知したconnectionを処理するためnet/httpパッケージのserveを呼びます

https://github.com/golang/go/blob/master/src/net/http/server.go#L3029

  1. main go routineでfor分を立ち上げます。tcp/ip通信のconnectionをずっと処理しておくためです。

https://github.com/golang/go/blob/master/src/net/http/server.go#L3058-L3090

  1. connectionが来たら、受け取ります

https://github.com/golang/go/blob/master/src/net/http/server.go#L3059

  1. 受け取ったconnectionを別なgoroutineをたちあげて、単独に処理します。

https://github.com/golang/go/blob/master/src/net/http/server.go#L3089

ここまではmain goroutineで行うものです

各connectionの処理流れ

ここからはmain goroutineではなく、別なgoroutineになります

  1. connのServeを呼びます

https://github.com/golang/go/blob/master/src/net/http/server.go#L1845-L1846

コメントのように各connectionに対する処理です

  1. 異例処理します

https://github.com/golang/go/blob/master/src/net/http/server.go#L1850-L1868

ここがあるから、各connectionに対して、ハンドリングするときに、nilエラーになるときに、panicされても、main go routineが止まりなく、走り続けられます。

  1. serverHandlerを作成し、serverHandlerのserveHttpを呼びます

https://github.com/golang/go/blob/master/src/net/http/server.go#L1995

  1. serverHandlerのserveHttpを呼びます

https://github.com/golang/go/blob/72fdecafc0c1863f7540bd827a387e7e56836187/src/net/http/server.go#L2915

4-1. handlerを用意していないばあい、defaultServeMuxをhanlderとして使います。

https://github.com/golang/go/blob/72fdecafc0c1863f7540bd827a387e7e56836187/src/net/http/server.go#L2917-L2919

サンプルコードにはNewHandler()を呼び出して、カスタマイズのhandlerを作っていますので、ここには入りません。

  1. handlerのserveHttpを呼びます

https://github.com/golang/go/blob/72fdecafc0c1863f7540bd827a387e7e56836187/src/net/http/server.go#L2936

ここまではnet/httpパッケージがやっていることです。

  1. カスタマイズのhandlerのserveHttpを呼びます。
func (h *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	fmt.Println(req.Header)

	writer.WriteHeader(200)
}

Discussion