技術メモ

技術メモ

ラフなメモ

GoのHTTPサーバーの実装

はじめに

Goは標準パッケージとしてHTTPサーバが組み込まれており、net/http パッケージを用いると簡単にHTTPサーバを動かすことができます。今回は net/http パッケージの一部(HTTPサーバの内容)の実装を読むことで、HTTPサーバが動く裏側を見てみたいと思います。困ったら 公式ドキュメント を見ましょう。

なお、読んでいるコード Go1.13 のものです。

Doc

まずざっと公式ドキュメントに書いてあるサンプルとドキュメントを眺めて、仕様をおさらいしておきます。

type Handler

ハンドラはHTTPリクエストに対してレスポンスを返します。

ServeHTTPはレスポンスヘッダーとデータをResponseWriterに書き込んでからreturnする必要があります。リクエストが終了したことを示すシグナルを返します。ServeHTTP呼び出しの完了後または完了と同時にResponseWriterを使用したり、Request.Bodyを読み取ることは無効です。

HTTPクライアントのソフトウェア、HTTPプロトコルバージョン、およびクライアントとGoで実装されたサーバー間のミドルウェアによっては、ResponseWriterに書き込んだ後にRequest.Bodyから読み取ることができない場合があります。 丁寧にハンドラを扱う場合は、最初にRequest.Bodyを読み取り、次にレスポンスを返す必要があります。

リクエストボディを読み取る場合を除き、ハンドラは提供されたリクエストを変更しないでください。

ServeHTTPがパニックになった場合、サーバー(ServeHTTPの呼び出し元)は、パニックの影響がアクティブなリクエストとは無関係であると仮定します。パニックを回復し、サーバーのエラーログにスタックトレースを記録し、HTTPプロトコルに応じてネットワーク接続を閉じるか、HTTP/2 RST_STREAMを送信します。ハンドラを中断して、クライアントには中断されたレスポンスが表示されますが、サーバーがエラーを記録しないようにするには、ErrAbortHandlerを用いてパニックを起こします。

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

ServeHTTP(ResponseWriter, *Request) を実装していれば Handler になることができます。

type HandlerFunc

type HandlerFunc func(ResponseWriter, *Request)

HandlerFunc 型は通常の関数を HTTP ハンドラとして扱えるようにするためのアダプターです。fが適切なシグネチャを持っている関数であれば、HanderFunc(f) はハンドラと振る舞うことができ、関数fを呼び出します。

func (HandlerFunc) ServeHTTP

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

ServeHTTPはf(w, r)を呼び出します。

HandlerFuncServeHTTP のように自身の関数を呼び出すことで Handler を満たしています。

type ServeMux

ServeMux は HTTP リクエストの URL と対応するハンドラを保持するマルチプレクサです。HTTP リクエストが来たときに、対応するハンドラを呼び出します。以下は GoDoc の翻訳です。

デフォルトだと DefaultServeMux という変数が ServeMux 型として定義されていて、このマルチプレクサが ListenAndServe が呼ばれたときに用いられます。

ServeMuxはHTTPリクエストのマルチプレクサです。要求されたリクエストのURLと登録されているURLのパターンのリストと比較し、URLに最も一致するパターンのハンドラを呼び出します。

パターン名は「/favicon.ico」といったルート化されたパスや「/images/」(末尾のスラッシュに注意)といったルート化されたサブツリーとして指定されます。 パターンは短いパターンよりも長いパターンが優先されます。そのため「/images/」と「/images/thumbnails/」というパターンがハンドラとして登録されている場合、「/images/thumbnails/」から始まるリクエストパスに対しては、後者のハンドラが呼び出されます。前者は「/images/」というサブツリーに含まれる任意のパスに対して、ハンドラが呼び出されます。

スラッシュで終わるパターンはルートのサブツリーとして定まるため、「/」というパターンは単に「/」というURLのパスにマッチするだけでなく、他の登録されていないパターンとマッチしないすべてのパスとマッチします。

サブツリーが登録されており、末尾がスラッシュなしでサブツリーのルートを指定するリクエストを受信した場合、ServeMuxはそのリクエストをサブツリーのルートにリダイレクトします(末尾のスラッシュを追加します)。この動作は、末尾にスラッシュなしのパスを登録することで上書きできます。たとえば、「/images/」を登録すると、「/images」が別に登録されていない限り、ServeMuxは「/images」に対するリクエストを「/images/」にリダイレクトします。

パターンはオプションでホスト名で始まり、そのホスト上のURLのみに一致するように制限できます。ホスト名が指定されているパターンは一般的なパターンよりも優先されるため、ハンドラは「 http://www.google.com/ 」のリクエストを引き継ぐことなく、2つのパターン「/codesearch」と「codesearch.google.com/」を登録できます。

ServeMuxはURLリクエストパスやホストヘッダーのサニタイズ、ポート番号の除去、...要素や繰り返されるスラッシュを含むリクエストのリダイレクト、URLの正規化も行います。

Examples

func ListenAndServe

func ListenAndServe(addr string, handler Handler) error

HTTP サーバを起動する関数です。以下は GoDoc の翻訳です。

ListenAndServeは、TCPネットワークアドレス addr をListenし、handler でServeを呼び出して、リクエストされたコネクション要求を処理します。受け付けられたコネクションは、TCPキープアライブを有効にするように構成されています。

ハンドラの引数は通常 nil です。この場合、DefaultServeMux が使用されます。

ListenAndServe は常に、nil 以外のエラーを返します。

  • ListenAndServe を使う実装例
package main

import (
    "io"
    "log"
    "net/http"
)

func main() {
    // Hello world, the web server

    helloHandler := func(w http.ResponseWriter, req *http.Request) {
        io.WriteString(w, "Hello, world!\n")
    }

    http.HandleFunc("/hello", helloHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func Handle

func Handle(pattern string, handler Handler)

Handle は、指定されたパターンとハンドラの対応を DefaultServeMux に登録します。ServeMux のドキュメントでは、パスに対応するパターンの選択方法について記載されています。

package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
)

type countHandler struct {
    mu sync.Mutex // guards n
    n  int
}

func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.mu.Lock()
    defer h.mu.Unlock()
    h.n++
    fmt.Fprintf(w, "count is %d\n", h.n)
}

func main() {
    http.Handle("/count", new(countHandler))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func HandleFunc

func HandleFuncfunc Handle のラッパーです。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

HandleFunc は、指定されたパターンのハンドラを DefaultServeMux に登録します。ServeMux のドキュメントでは、パスに対応するパターンの選択方法について記載されています。

package main

import (
    "io"
    "log"
    "net/http"
)

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

    http.HandleFunc("/", h1)
    http.HandleFunc("/endpoint", h2)

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

見通しを良くするために

同じ名前でも、型の名前なのか、関数の名前なのか、型に紐づくメソッド名なのかに注意する必要があります。また Handle なのか、Handler なのか、注意が必要です。

上記の GoDoc の説明ではすでに登場していますが、HTTP サーバを利用するユーザとしては以下の 3 つの型を中心に考えると、構成が見やすくなりそうです。

type ServeMux

  • URLと対応するハンドラを保持するマルチプレクサ。HTTPリクエストが来たときに、対応するハンドラを呼び出します。
  • デフォルトで DefaultServeMux という変数が定義されていて、これを利用します。
  • NewServeMux() でデフォルト以外のマルチプレクサを生成できます。

type Handler

  • ハンドラです。
  • 直感的には、リクエストに対してレスポンスを返す関数、と捉えて良いと思います。
  • マルチプレクサにハンドラを登録します。
  • ServeHTTP メソッドを満たす型(func(ResponseWriter, *Request) 型)がハンドラになることができます。

type HandlerFunc

  • type HandlerFunc func(ResponseWriter, *Request)
  • パスに対してハンドラを紐付ける です。

type Handlertype HandlerFunc の関係性というか、なぜ type HandlerFunc が存在するか、という背景の理解は ruiu さんの記事が参考になりました。

Implementation

ここからは標準パッケージの実装を見てみます。

1. HandleFunc 関数

1-1. HandleFunc

まず HandleFunc 関数を見てみます。HandleFunc 関数はデフォルトでハンドラ関数を DefaultServeMux 変数に登録するのでした。実装は以下のようになっています。DefaultServeMux 変数の HandleFunc へのラッパーになっています。

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

DefaultServeMux 変数とはどのような構造体になっているか確認すると、以下の実装からわかるように *ServeMux 型の変数です。defaultServeMux はゼロ値で初期化されていて DefaultServeMux はその初期化された値へのポインタを保持しています。ServeMux 構造体はリクエストのパスとハンドラをマッピングする m のフィールドが本質のように見えます。

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
}

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

type muxEntry struct {
    h       Handler
    pattern string
}

1-2. ServeMux.HandleFunc

*ServeMux 型がレシーバの HandleFunc 関数は以下のようになっています。このメソッドも Handle メソッドへのラッパーになっています。

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    // handler を HandlerFunc 型に変換して Handle を呼び出す
    // 変換(Conversions)の仕様は https://golang.org/ref/spec#Conversions を参照
    mux.Handle(pattern, HandlerFunc(handler))
}

1-3. ServeMux.Handle

では ServeMux 型の Handle メソッドの実装を見てみると、以下のようになっています。この実装で ServeMux 構造体のフィールドに値をセットしています。

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern")
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if _, exist := mux.m[pattern]; exist {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    
    // パスとハンドラの構造体 muxEntry の値を map へ登録
    e := muxEntry{h: handler, pattern: pattern}
    mux.m[pattern] = e
    
    // TODO: 存在する理由がよくわからず
    if pattern[len(pattern)-1] == '/' {
        mux.es = appendSorted(mux.es, e)
    }

    // TODO: よくわかっていない
    if pattern[0] != '/' {
        mux.hosts = true
    }
}

2. ListenAndServe 関数

次に ListenAndServe を見てみます。

2-1. ListenAndServe

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

ここで生成している Server 構造体で以下で、HTTPサーバとして動作するときのパラメータを定義する構造体です。ListenAndServe ではListenするアドレスとハンドラのみを初期化するようになっています。構造体のコメントにもあるように Handler 変数は nil であれば http.DefaultServeMux がセットされます。

// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
    Addr    string  // TCP address to listen on, ":http" if empty
    Handler Handler // handler to invoke, http.DefaultServeMux if nil

    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

    disableKeepAlives int32     // accessed atomically.
    inShutdown        int32     // accessed atomically (non-zero means we're in Shutdown)
    nextProtoOnce     sync.Once // guards setupHTTP2_* init
    nextProtoErr      error     // result of http2.ConfigureServer if used

    mu         sync.Mutex
    listeners  map[*net.Listener]struct{}
    activeConn map[*conn]struct{}
    doneChan   chan struct{}
    onShutdown []func()
}

2-2. Server.ListenAndServe

ServerListenAndServe は以下です。

// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
        return ErrServerClosed
    }
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    // TCP のリスナーを生成
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(ln)
}

2-3. Server.Serve

続いて srv.Serve(ln) として実装されている Serve メソッドを見てみます。

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
    if fn := testHookServerServe; fn != nil {
        fn(srv, l) // call hook with unwrapped listener
    }

    origListener := l
    l = &onceCloseListener{Listener: l}
    defer l.Close()

    if err := srv.setupHTTP2_Serve(); err != nil {
        return err
    }

    if !srv.trackListener(&l, true) {
        return ErrServerClosed
    }
    defer srv.trackListener(&l, false)

    var tempDelay time.Duration // how long to sleep on accept failure

    baseCtx := context.Background()
    if srv.BaseContext != nil {
        baseCtx = srv.BaseContext(origListener)
        if baseCtx == nil {
            panic("BaseContext returned a nil context")
        }
    }

    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        // リスナーの接続要求を待つ(ブロッキング)
        rw, e := l.Accept()
        if e != nil {
            select {
            case <-srv.getDoneChan():
                return ErrServerClosed
            default:
            }
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        if cc := srv.ConnContext; cc != nil {
            ctx = cc(ctx, rw)
            if ctx == nil {
                panic("ConnContext returned nil")
            }
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        // ゴルーチンで処理
        go c.serve(ctx)
    }
}

2-4. conn.serve

リクエストをAcceptすると、ゴルーチンとして動作する、リクエストを処理するメソッドを呼び出します。呼び出した後は再度 l.Accept() として次のリクエストを待ち受けます。

リクエストを処理する serve メソッドです。いろいろ処理があって長いのですが、わかりやすく色々省くと以下のように見ることができます。conn.readRequest のメソッドを追っていくと RFC に沿って HTTP リクエストの構文を解析する処理が見れるのですが、(説明しきれないので)ここでは省きます。

func (c *conn) serve(ctx context.Context) {
    // ...
    for {
        w, err := c.readRequest(ctx)
        // ...
        serverHandler{c.server}.ServeHTTP(w, w.req)
        // ...
    }
}

メソッドの完全な実装は以下です。長いです。

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
    defer func() {
        if err := recover(); err != nil && err != ErrAbortHandler {
            const size = 64 << 10
            buf := make([]byte, size)
            buf = buf[:runtime.Stack(buf, false)]
            c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
        }
        if !c.hijacked() {
            c.close()
            c.setState(c.rwc, StateClosed)
        }
    }()

    if tlsConn, ok := c.rwc.(*tls.Conn); ok {
        if d := c.server.ReadTimeout; d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
        }
        if d := c.server.WriteTimeout; d != 0 {
            c.rwc.SetWriteDeadline(time.Now().Add(d))
        }
        if err := tlsConn.Handshake(); err != nil {
            // If the handshake failed due to the client not speaking
            // TLS, assume they're speaking plaintext HTTP and write a
            // 400 response on the TLS conn's underlying net.Conn.
            if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
                io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
                re.Conn.Close()
                return
            }
            c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
            return
        }
        c.tlsState = new(tls.ConnectionState)
        *c.tlsState = tlsConn.ConnectionState()
        if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
            if fn := c.server.TLSNextProto[proto]; fn != nil {
                h := initNPNRequest{ctx, tlsConn, serverHandler{c.server}}
                fn(c.server, tlsConn, h)
            }
            return
        }
    }

    // HTTP/1.x from here on.

    ctx, cancelCtx := context.WithCancel(ctx)
    c.cancelCtx = cancelCtx
    defer cancelCtx()

    c.r = &connReader{conn: c}
    c.bufr = newBufioReader(c.r)
    c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

    for {
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // If we read any bytes off the wire, we're active.
            c.setState(c.rwc, StateActive)
        }
        if err != nil {
            const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"

            switch {
            case err == errTooLarge:
                // Their HTTP client may or may not be
                // able to read this if we're
                // responding to them and hanging up
                // while they're still writing their
                // request. Undefined behavior.
                const publicErr = "431 Request Header Fields Too Large"
                fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
                c.closeWriteAndWait()
                return

            case isUnsupportedTEError(err):
                // Respond as per RFC 7230 Section 3.3.1 which says,
                //      A server that receives a request message with a
                //      transfer coding it does not understand SHOULD
                //      respond with 501 (Unimplemented).
                code := StatusNotImplemented

                // We purposefully aren't echoing back the transfer-encoding's value,
                // so as to mitigate the risk of cross side scripting by an attacker.
                fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s%sUnsupported transfer encoding", code, StatusText(code), errorHeaders)
                return

            case isCommonNetReadError(err):
                return // don't reply

            default:
                publicErr := "400 Bad Request"
                if v, ok := err.(badRequestError); ok {
                    publicErr = publicErr + ": " + string(v)
                }

                fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
                return
            }
        }

        // Expect 100 Continue support
        req := w.req
        if req.expectsContinue() {
            if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
                // Wrap the Body reader with one that replies on the connection
                req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
            }
        } else if req.Header.get("Expect") != "" {
            w.sendExpectationFailed()
            return
        }

        c.curReq.Store(w)

        if requestBodyRemains(req.Body) {
            registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
        } else {
            w.conn.r.startBackgroundRead()
        }

        // HTTP cannot have multiple simultaneous active requests.[*]
        // Until the server replies to this request, it can't read another,
        // so we might as well run the handler in this goroutine.
        // [*] Not strictly true: HTTP pipelining. We could let them all process
        // in parallel even if their responses need to be serialized.
        // But we're not going to implement HTTP pipelining because it
        // was never deployed in the wild and the answer is HTTP/2.
        // 
        // serverHandler を生成して ServeHTTP を呼び出す
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()
        if !w.shouldReuseConnection() {
            if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                c.closeWriteAndWait()
            }
            return
        }
        c.setState(c.rwc, StateIdle)
        c.curReq.Store((*response)(nil))

        if !w.conn.server.doKeepAlives() {
            // We're in shutdown mode. We might've replied
            // to the user without "Connection: close" and
            // they might think they can send another
            // request, but such is life with HTTP/1.1.
            return
        }

        if d := c.server.idleTimeout(); d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
            if _, err := c.bufr.Peek(4); err != nil {
                return
            }
        }
        c.rwc.SetReadDeadline(time.Time{})
    }
}

serverHandlerServer へのポインタを保持しているのみです。定義されているメソッドも ServeHTTP だけです。実際の処理は *Server 型の srv 変数が実装している ServeHTTP に移譲しています。

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
    srv *Server
}

2-5. serverHandler.ServeHTTP

マルチプレクサ(指定しなかった場合は DefaultServeMux )からリクエストに対応するハンドラを取得して、そのハンドラの ServeHTTP を呼び出しています。

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    // ここで Server のハンドラが nil の場合に DefaultServeMux をセットしている
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

2-5-1. ServeMux.ServeHTTP

serverHandler.ServeHTTP の一番最後の部分の中身を確認します。

handler.ServeHTTP(rw, req)

少し戻って ServeMux 型は ServeHTTP を実装しているので Handler インターフェースを満たしています。

この ServeMux.ServeHTTP メソッドでは *ServeMux に登録されているハンドラをリクエストから取得して、そのハンドラの ServeHTTP を呼び出します。

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

2-5-2. ServeMux.Handler

ServeMuxHandler メソッドは以下のようになっていて、Handler インターフェースを返すメソッドです。ざっくり ServeMux で保持しているハンドラを取得するためのメソッドです。

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

    // CONNECT requests are not canonicalized.
    if r.Method == "CONNECT" {
        // If r.URL.Path is /tree and its handler is not registered,
        // the /tree -> /tree/ redirect applies to CONNECT requests
        // but the path canonicalization does not.
        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
        }

        return mux.handler(r.Host, r.URL.Path)
    }

    // All other requests have any port stripped and path cleaned
    // before passing to mux.handler.
    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)

    // If the given path is /tree and its handler is not registered,
    // redirect for /tree/.
    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    }

    if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        url := *r.URL
        url.Path = path
        return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    }

    return mux.handler(host, r.URL.Path)
}

2-5-3. ServeMux.handler

// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    // Host-specific pattern takes precedence over generic ones
    //
    // ServeMux が保持しているmap からパスに対応するハンドラを取得
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

2-5-4. ServeMux.match

上記のメソッドの中で呼び出されている match メソッドです。パスからハンドラを探索して、呼び出し元に返します。

// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    // Check for exact match first.
    v, ok := mux.m[path]
    if ok {
        return v.h, v.pattern
    }

    // Check for longest valid match.  mux.es contains all patterns
    // that end in / sorted from longest to shortest.
    for _, e := range mux.es {
        if strings.HasPrefix(path, e.pattern) {
            return e.h, e.pattern
        }
    }
    return nil, ""
}

まとめ

net/http のHTTPサーバのリクエストを処理してハンドラを呼び出して、レスポンスを返す実装についてざっと見てみました。