GolangのHTTPサーバーのコードリーディング

Golang

アプリケーション

2021-06-30 23:23:08

概要

Goでrouterを作ったときにHTTPサーバーのコードの内部を読んだので、その時のメモ。

github.com - bmf-san/goblin

HTTPサーバーのコードリーディング

基本形

Goに入門したとによく見るであろう形のコード。

色々なものが省略されてこの形になっている。

package main

import (
    "net/http"
)

func main() {
    http.HandlerFunc("/index", func(w http.ReponseWriter, req *http.Request) {
        w.Write([]byte("hello world"))
    })

    http.ListenAndServe(":8080")
}

省略しないで丁寧に書いた形

先程の基本形を省略しないで書いた形。
どのような実装を経て基本形になるのか1つずつ確認していく。

package main

import (
    "net/http"
)

func main() {
    // マルチプレクサ。URLマッチングをするための構造体。静的なルーティングのみ解決する。
    mux := http.NewServeMux()
    ih := new(indexHandler)
    // muxにルーティングを登録する
    mux.Handle("/index", ih)

    srv := http.Server{
        Addr: ":8080",
        Handler: mux,
    }

    srv.ListenAndServe()
}

// Handlerインターフェースを実装した構造体。
type indexHandler struct{}

// ServeHTTPを実装
func (i *indexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("helo world"))
}

Handlerの置き換え

まずは、Handlerの置き換え。

ServeHTTPは関数型のaliasであるHandlerFuncに置き換えることができる。

cf.


package main

import (
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    // ただの関数をHandlerFunc型にキャストすれば良い。Handlerインターフェースを満たせる。
    mux.Handle("/index", http.HandlerFunc(indexHandler))

    s := http.Server{
        Addr:    ":8080",
        Handler: m,
    }
    s.ListenAndServe()
}

// 適当な構造体を用意して、ServeHTTPを実装しなくても良い。
func indexHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello world"))
}

DefaultServeMuxの利用

muxをDefaultServeMuxで代用。

DefaultServeMuxはServeMux型の構造体を持っている。

HandlerFuncというmuxにルーティングを登録する関数を実装している。

cf.

package main

import (
    "net/http"
)

func main() {
    // muxを作らなくてもこれだけでOK
    http.HandleFunc("/index", indexHandler)

    s := http.Server{
        Addr: ":3000",
        // net/httpがデフォルトで持っている変数。DefaultServeMuxはServeMux型の構造体を持っている。HandlerFuncというmuxにルーティングを登録する関数を実装している。
        Handler: http.DefaultServeMux,
    }
    s.ListenAndServe()
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello world"))
}

ListenAndServe()の利用

Server構造体(http.Server{})を作らずとも、ListenAndServe()を代用することができる。

cf.

package main

import (
    "net/http"
)

func main() {
    http.HandlerFunc("/index", func(w http.ReponseWriter, req *http.Request) {
        w.Write([]byte("hello world"))
    })

    // Server構造体(http.Server{})を作らなくても大丈夫
    http.ListenAndServe(":8080")
}

これで最初の基本形に到達。

まとめ

APIサーバー作るときとか普段余り意識しないと思うが、知っておくと何か拡張したいときに役に立つ、はず。

routerを作るときはhttp.Handlerのインターフェースを意識してmuxを作って上げれば良い。

元ネタ

GoでRouter自作実装寄りな話

About the author

Image

bmf san @bmf_san
A web developer in Japan.