Chapter 05

ハンドラによるレスポンス返却の詳細

さき(H.Saki)
さき(H.Saki)
2021.09.19に更新

この章について

前2章を使って、

  • httpサーバーを起動し、
  • DefaultServeMuxを使って、リクエストを適切なハンドラにルーティングする

ところまで追ってきました。

この章では、ルーティング後の話「ハンドラ内でどのようにしてレスポンスを作成し、返しているのか」について説明します。

ハンドラ関数のおさらい

おさらいとして、ユーザーがサーバーに登録するハンドラの形をもう一度見てみます。

func main() {
	h1 := func(w http.ResponseWriter, _ *http.Request) {
		io.WriteString(w, "Hello from a HandleFunc #1!\n")
	}

	http.HandleFunc("/", h1) // パス"/"に、ハンドラh1が対応

	log.Fatal(http.ListenAndServe(":8080", nil))
}

ハンドラh1は、func(w http.ResponseWriter, _ *http.Request)というシグネチャをしています。

第二引数は、ハンドラが処理するリクエストが、http.Request型の形で入っているのだろうなと容易に想像がつきます。
そのため、ここでは第一引数であるhttp.RewponseWriterに注目します。

第一引数 - http.ResponseWriter

定義

type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(statusCode int)
}

出典:pkg.go.dev - net/http#ResponseWriter

http.RewponseWriterは、上記3つのメソッドを持つインターフェース型として定義されています。

ここで一つ疑問が生じます。
ハンドラが受け取るhttp.RewponseWriter型第一引数の、実体型は何になるのでしょうか。

これはインターフェースです。これを満たす実体は何でしょうか。

http.ResponseWriterインターフェースの実体型

http.ResponseWriterインターフェースの実体型を探すためには、http.ListenAndServe関数を呼んでから、この個別ハンドラのServeHTTPメソッドが呼ばれるまでの変数の流れを順に追っていく必要があります。

以下の図は、それをまとめたものです。

ここから、図の下部にあるhttp.ResponseWriterの大元は、2章前のreadRequestメソッドにて登場したhttp.response型だということがわかります。

http.response

このhttp.response型の中には、サーバー起動の際に取得したnet.Connが含まれています。

// A response represents the server side of an HTTP response.
type response struct {
	conn	*conn
	req	*Request // request for this response
    // (以下略)
}

// A conn represents the server side of an HTTP connection.
type conn struct {
    server *Server
    rwc net.Conn
    // (以下略)
}

そのため、http.response.Write()メソッドを呼ばれたときに実行されるのは、現在のコネクションであるnet.ConnWriteメソッドとなります。

したがって、

h1 := func(w http.ResponseWriter, _ *http.Request) {
    io.WriteString(w, "Hello from a HandleFunc #1!\n")
}

のようにhttp.ResponseWriterに書き込まれた内容は、ネットワークを通じて返却するレスポンスへの書き込みとなるわけです。

(210919追記)

Hiroaki Nakamura(@hnakamur2)さんから、「http.response.Write()メソッドを呼んだ後にネットワーク書き込みにたどり着くまで」についての補足情報をいただきました。(ツイートリンク)

  1. (コード出典)
    非公開のhttp.response.write()メソッドが呼ばれる
  2. (コード出典)
    その中で、http.response型内部にあるbufio.WriterWriteメソッドが呼ばれる
  3. (コード出典)
    http.response型内部のbufio.Writerインターフェースの具体型は、本記事3章http.response型を取得するときに呼んだhttp.conn.readRequestメソッドにて、http.response.cwフィールド(http.chunkWriter型)がセットされている
  4. (コード出典)
    http.chunkWriter型のWriteメソッドにてネットワーク書き込みが行われ、このWriteメソッドの中身を掘っていくとnet.Conn.Writeメソッドにたどり着く

ということです。情報ありがとうございました。