MyDocs

# Golangのhttp/netについて調べた

サーバ

何もしないサーバを起動。

package main

import "net/http"

func main() {
	http.ListenAndServe("", nil)
}

ListenAndServe はネットワークアドレスとハンドラを引数で受け取る。ハンドラが nil の場合は DefaultServerMux が使われる。

Server の構造体はこんな感じ。

type Server struct {
	Addr string
	Handler Handler
	TLSConfig *tls.Config
	ReadTimeout time.Duration
	ReadHeaderTimeout time.Duration
	WriteTimeout time.Duration
	IdleTimeout time.Duration
	MaxHeaderBytes int
	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
	ConnState func(net.Conn, ConnState)
	ErrorLog *log.Logger
	BaseContext func(net.Listener) context.Context
	ConnContext func(ctx context.Context, c net.Conn) context.Context
	inShutdown atomicBool
	disableKeepAlives int32
	nextProtoOnce     sync.Once
	nextProtoErr      error
	mu         sync.Mutex
	listeners  map[*net.Listener]struct{}
	activeConn map[*conn]struct{}
	doneChan   chan struct{}
	onShutdown []func()
}

設定を変更する場合は Server の構造体に値を指定する。

func main() {
	server := http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: nil,
	}
	server.ListenAndServe()

ハンドラとハンドラ関数

ハンドラがない場合すべてのリクエストで 404 を返す。

ハンドラとは、 ServerHTTP メソッド を持ったインターフェースのことを指す。 このメソッドは、 インターフェース HTTPResponseWriter構造体 Request のポインタ の 2 つの引数を取る。 つまり、 ServerHTTP(http.ResponseWriter, *http.Request) をもつインターフェースがハンドラになる。

type HelloHandler struct{}

func (h *HelloHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "hello")
}

func main() {
	helloHandler := new(HelloHandler)
	server := http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: helloHandler,
	}
	server.ListenAndServe()
}

この状態だと、すべてのアクセスが 1 つのハンドラに行く。

実際は URL ごとに異なるハンドラで処理する必要があるため Handler フィールドに値を指定しない ( デフォルトで DefaultServerMux を使う ) 。

type HelloHandler struct{}
type WorldHandler struct{}

func (h *HelloHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "hello")
}
func (w *WorldHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "world")
}

func main() {
	helloHandler := new(HelloHandler)
	worldHandler := new(WorldHandler)
	server := http.Server{
		Addr: "0.0.0.0:8080",
	}
	http.Handle("/hello", helloHandler)
	http.Handle("/world", worldHandler)
	server.ListenAndServe()
}

http.Handle は実際には DefaultServeMux の Handle メソッドを呼び出している。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

ハンドラとは、 ServerHTTP(http.ResponseWriter, *http.Request) をもつインターフェースのこと。 ハンドラ関数とは、ハンドラのように振る舞う関数。リクエストのポインタを受け取ることができる。

package main

import (
	"fmt"
	"net/http"
)

type HelloHandler struct{}
type WorldHandler struct{}

func (h *HelloHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "hello")
}
func (w *WorldHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "world")
}

func hello(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "hello")
}
func world(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(writer, "world")
}

func main() {
	helloHandler := new(HelloHandler)
	worldHandler := new(WorldHandler)
	server := http.Server{
		Addr: "0.0.0.0:8080",
	}
	http.Handle("/hello", helloHandler)
	http.Handle("/world", worldHandler)
	http.HandleFunc("/hello2", hello)
	http.HandleFunc("/world2", world)
	server.ListenAndServe()
}

何が違うのか

やってることは DefaultServeMux.Handle(pattern, handler) なので同じ。 既存のインターフェースがある場合は、 ServeHTTP メソッドを追加するだけでハンドラになるからそういった場合は、 http.Handle のほうが手軽。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

サードパーティー製のマルチプレクサ

標準でも特に辛くない。 request.Method でメソッドも取得できるのでかなり使い勝手もいい。

mux.HandleFunc("/users/create", createUsers)
mux.HandleFunc("/users/read/", readUsers)
mux.HandleFunc("/users/update/", updateUsers)
mux.HandleFunc("/users/delete/", deleteUsers)

func readerUsers(writer http.ResponseWriter, request *http.Request) {
	sub := strings.TrimPrefix(request.URL.Path, "/users/read/")
	userId, err := strconv.Atoi(sub) // これで /users/read/:id の id を取得できる
}

下記のような、 /users/:user_id/tasks/:task_id みたいに解析を結構頑張らないといけない場合は結構辛い。

mux.HandleFunc("/users/2/tasks/create", createUsers)
mux.HandleFunc("/users/2/tasks/read/", readUsers)
mux.HandleFunc("/users/2/tasks/update/", updateUsers)
mux.HandleFunc("/users/2/tasks/delete/", deleteUsers)

この辺が良さそう。