Go言語のhttpパッケージ周りについて整理
背景
この記事で静的ファイルサーブをすることになったが,普通に
localhost:8080
を開くのと同時にやる方法がわからなかったから整理する.
localhost:8080開くだけのコード
package main
import (
"log"
"net/http"
)
func main() {
port := "8080"
http.Handle("/", http.FileServer(http.Dir("../docs/")))
log.Printf("Listen on port: %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
静的ファイルサーブだけするコード
package main
import (
"net/http"
)
func main() {
mux := http.NewServeMux()
// 公開したいディレクトリを指定
fileServer := http.FileServer(http.Dir("./src3"))
// /staticに公開する
mux.Handle("/static/", http.StripPrefix("/static/", fileServer))
server := http.Server{
Addr: ":8080",
Handler: mux,
}
server.ListenAndServe()
}
これによって次のように指定したローカルなファイルをブラウザ上におくことができる.
ただし,これだけではlocalhost:8080にアクセスすると次のようになる.
ただ単に同時にlocalhost:8080だけを開くコードを動かそうとすると以下のようになってしまう.
server % go run server.go
2022/11/15 10:13:38 Listen on port: 8080
2022/11/15 10:13:38 listen tcp :8080: bind: address already in use
exit status 1
これら二つをいい感じに融合させる必要がある.
ただ,今まで呪文的に何も考えずこれらを使っていて各部分の詳しいことには目を向けていなかったので,それを復習しながら考える.
http.Handleとは
http.Handle("/", http.FileServer(http.Dir("../docs/")))
の部分を見ていく.
まず,構造は次のようになっている.
func Handle(pattern string, handler Handler) {
DefaultServeMux.Handle(pattern, handler)
}
http.Handle は表示する URL と、URL に対応する http.Handler を DefaultServeMux に登録する関数である.
http.Handlerとは
http.Handler は ServeHTTP 関数を持つだけのインターフェイスで HTTP リクエストを受けてレスポンスを返す
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
ServerHTTP関数
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
// 一部抜粋
handler := sh.srv.Handler
handler.ServeHTTP(rw, req)
}
https://github.com/golang/go/blob/master/src/net/http/server.go#L2867
ここの2915行あたりに抜粋してないのは書いてある(内容変更されていたらずれてるかも).
sh.srv.Handlerで、http.Handler型インターフェースを得る
Handlerインターフェースのメソッド、ServeHTTPを呼ぶ
DefaultServeMux とは
DefaultServeMuxは、net/httpパッケージ内に存在する公開グローバル変数.
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
ServeMux型の型定義は次のようになっている.
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
DefaultServeMuxの役割は、ServeMuxのmフィールドが中心部分で,mフィールドのmapには、「URLパス(開発者がhttp.HandleFunc関数で登録したハンドラ関数)」の対応関係が格納されているルーターのようなもの.
Goのhttpサーバーは、http.ListenAndServeの第二引数nilの場合ではDefaultServeMux内に格納された情報を使って、ルーティングを行っている
この記事に,DefaultServeMuxに開発者が書いたハンドラが登録されるまでの流れなどが説明されている.
http.FileServer
では,handlerに渡しているhttp.FileServer()について見てみる.
func FileServer(root FileSystem) Handler
http.FileServerはHandlerを返し,そのHandlerはルートにあるファイルシステムの内容をHTTPリクエストに返す.
引数となっているFileSystemの中身は次のようになっている.
type FileSystem interface {
Open(name string) (File, error)
}
冷静になってよく見てみれば,最初にあげた2つのコードのどちらもフォルダの指定にこのメソッドを使っている.
それならmelody.txtをdocsに置けばいいのではということになる.
実際置いてみると正しく静的ファイルサーブできた.