golang net/http のコードリーディング
Handler interface
ServeHTTP(ResponseWriter, *Request): header, data を ResponseWriter に書き込んで返却
signal を送ってリクエストは終了する
メソッド完了後に ReponseWriter を使ったり、メソッド完了後にRequest.Bodyを読むのは不正
ResponseWriter に書き込んでから Request.Body を読むのは無理げ
まず Handler は先に Request.Body を読み返却する
body を読む以外 handler は提供された Request を更新すべきではない
メソッドがactive requestでpanicになるとリカバーしサーバエラーログにスタックトレースを表示
同時にネットワーク接続を閉じて HTTP/2, RST_STREAM を送信する
クライアントには中断のレスポンスを返すがエラーを記録しない場合は ErrAbortHandler で panic させる
ResponseWriter interface
HTTPリクエストを処理するハンドラがこのインターフェースを満たすことで、HTTPレスポンスを生成し、クライアントに送信できる
Handler.ServeHTTP メソッドが返したあとには ResponseWriter は使用できない
Header() Header
tkdn.icon Header の実装をみているがすべて値レシーバになっており気になった > ポインタレシーバと値レシーバの使い分けの理解が浅い
WriteHeader による header map を返す. 自動で付与されるヘッダを潰す場合には set nil
tkdn.icon HTTP Trailers という新しい概念を知った
Write:WriteHeader呼び出し前にデータの書き込み(http.StatusOk)を行う
WriteHeader: 指定されたステータスコードでHTTPレスポンスヘッダーを送信
Flusher interface
HTTPハンドラがバッファリングしているデータをクライアントにフラッシュできる
flush()
Hijacker interface
Hijack()メソッドを呼び出すことで、呼び出し元が接続を制御. HTTPサーバーライブラリは接続に対して何も行いません. Hijackを呼び出した後は、接続を管理し、閉じる責任が呼び出し元に移る
CloseNotifier interface
Deprecatedっぽい -> New code should use Request.Context instead.
conn struct
hijack 関連のメソッド
chunkWriter struct
responseのconnバッファに書き込みを行うものであり、response.wのバッファライターによってラップされる
大事っぽい
HTTPレスポンスのバッファへの書き込みを管理し、Headerのファイナライズも担当、Content-TypeとContent-Lengthの条件付き設定、chunkedモードでのchunkヘッダーの追加なども行う。(*response).Writeのコメントにも言及されているように、この型はHTTPレスポンスの書き込みフロー全体において重要な役割を果たします
res, header()
と cw.メソッド群が列挙
内部で重要な役割を担っている
response struct
サーバサイドのHTTPレスポンスの型
handlerDone atomic.Bool atomic.Bool is 何になってしまった
code:Bool.go
type Bool struct {
_ noCopy
v uint32
}
TralerPrefix で strings.CutPrefix(word, prefix) 覚えた
tkdn.iconSetReadDeadline/SetWriteDeadline がいまいち理解できていない
読み取り・書き込みの期限を設定する
connReader struct
code:cr.go
// sync.Cond の利用がわかった
cr.cond = sync.NewCond(&cr.mu)
cr.cond.Wait()
cr.cond.Broadcast()
tkdn.iconfor _, v := range [...]time.Duration
...の構文がわからないなんだろう
expectContinueReader struct
tkdn.iconWriter/Readerの種類が多いので混乱してきている
code:readReq.go
func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
// これは再帰的に呼び出しているけどポインタレシーバのメソッドとして呼んでる
req, err := readRequest(c.bufr)
}
httpguts.ValidHeaderFieldName, httpguts.ValidHeaderFieldValue
準標準ライブラリに依存している
delete(req.Header, "Host")
A server MUST respond with a 400 (Bad Request) status code to any
HTTP/1.1 request message that lacks a Host header field and to any
request message that contains more than one Host header field or a
Host header field with an invalid field-value.
(*chunkWriter).WriteHeader : ステータスコードチェックや *response.wroteHeader flag などを変えている
けっこうなクソデカメソッドかもしれない
途中書き込み済などの確認をする response struct のフィールドは多い
tkdn.iconw := res のように w を使用することがあるが writer ? なのかな
tkdn.iconTrailer header ってなんぞってなるな
HTTPのセマンティクスの仕様(URL)では、HTTPボディを送ったあとにあとからヘッダ(正確にはFieldと呼ぶ)を送ることができます。これをTrailerと呼びます。 response.req.Body = io.ReadCloser interface を指している -> コード上では switch type assertion をしている
コールドリーディングしていると、Union 型がないということを印象が強く残る。switch による type assertion を見ているときに式に与える interface がどういった型であるかは実際にビルドにしてみないとわからないのかも。各 case 節にはいったあとの、type narrowing は効くようになっている
(*response).Write
ここにかなり重要なことが書いていて、各種 Writer のチェーンは以下のようになる:
*response(ResponseWriter)->
(response).w、bufferBeforeChunkingSizeバイトのbufio.Writer ->
chunkWriter.Writer ->
writeHeader: Content-Length/Typeを最終化し、必要ならチャンクヘッダーを書き込む
conn.bufw、デフォルト(4KB)バイトの*bufio.Writer、書き込み先 ->
checkConnErrorWriter{c}、Write時の非nilエラーを記録し、それがあればc.werrにそれを埋め込みますが、それ以外の場合は次に書き込み->
rwc、net.Conn
w.w.Write といった記述もあり何Writerかがわかるとさらに読みやすい
ステータスコード
tkdn.icon まったく関係ないがプロトコルのことを proto といっている func validNextProto(proto string)
tkdn.icon ソースコード上には ConnState もありステータスコードとは違う
(*conn).serve(ctx context.Context)
ここも重要そう
tkdn.iconExpect: 100-continue ってヘッダがあるのを初めて知った
func (w *response) CloseNotify() <-chan bool
tkdn.icon戻り値の型で初出が出てきた。<- chan bool
アプリコードであまり出てこないかもしれない
HandlerFunc:
404の一連を見るとわかりやすいかも
code:server.go
type Handler interface {
ServeHTTP(ResponseWriter, *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)
}
// NotFound replies to the request with an HTTP 404 not found error.
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
// NotFoundHandler returns a simple request handler
// that replies to each request with a “404 page not found” reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
ServeMux struct:
URLに最も近いパターンのハンドラを起動する multiplexer
Handler と URL パターンの構造体
code:mux.go
type muxEntry struct {
h Handler
pattern string
}
ハンドラは http.ServeMux 型で受けるか http.HndlerFunc で受けるか
code:handler.go
// 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)
/users -> /users/setting, /users などの振り分けを考えるとよくと
/users は ServerMux が取り回し、/users/* 以下のパスを HandleFunc で処理する
tkdn.iconinterface
目的は(ライブラリとアプリコードでは面構えがちょっと違うと思うが)頻出するようなイディオムに慣れたいのと、コードをまずは読み取る訓練をするためだったのでまあよいかという感じ
struct は頻出だと思うが、(* pntr).foo.(type) アサーション使う場面ってそこまでないのかね。ライブラリに出てくる interface としてはアサーションで使う場面が多そうで、アプリコードの場合はテストでモックに差し替えたいの場面がありそうという所感をもった